<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 5.2.0">


  <link rel="apple-touch-icon" sizes="180x180" href="/murmur/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/murmur/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/murmur/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/murmur/images/logo.svg" color="#222">

<link rel="stylesheet" href="/murmur/css/main.css">



<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.1/css/all.min.css">
  <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/animate.css@3.1.1/animate.min.css">
  <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/nprogress@0.2.0/nprogress.css">
  <script src="//cdn.jsdelivr.net/npm/nprogress@0.2.0/nprogress.js"></script>

<script class="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"gitee.com","root":"/murmur/","scheme":"Gemini","version":"8.0.2","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":true,"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":"disqus","storage":true,"lazyload":false,"nav":null},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果：${query}","hits_time":"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）","hits":"找到 ${hits} 个搜索结果"}};
  </script>

  <meta property="og:type" content="website">
<meta property="og:title" content="汇话板">
<meta property="og:url" content="https://gitee.com/lianghuilin/murmur.git/page/4/index.html">
<meta property="og:site_name" content="汇话板">
<meta property="og:locale" content="zh_CN">
<meta property="article:author" content="William Leung">
<meta name="twitter:card" content="summary">


<link rel="canonical" href="https://gitee.com/lianghuilin/murmur.git/page/4/">


<script class="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : true,
    isPost : false,
    lang   : 'zh-CN'
  };
</script>

  <title>汇话板</title>
  


  <script>
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "https://hm.baidu.com/hm.js?a49081c570a782a242cdf6e19e1a584d";
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
  </script>




  <noscript>
  <style>
  body { margin-top: 2rem; }

  .use-motion .menu-item,
  .use-motion .sidebar,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header {
    visibility: visible;
  }

  .use-motion .header,
  .use-motion .site-brand-container .toggle,
  .use-motion .footer { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle,
  .use-motion .custom-logo-image {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line {
    transform: scaleX(1);
  }

  .search-pop-overlay, .sidebar-nav { display: none; }
  .sidebar-panel { display: block; }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/murmur/" class="brand" rel="start">
      <i class="logo-line"></i>
      <h1 class="site-title">汇话板</h1>
      <i class="logo-line"></i>
    </a>
      <p class="site-subtitle" itemprop="description">设定目标，放慢步调，用温和的力量自我改革</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul class="main-menu menu">
        <li class="menu-item menu-item-home">

    <a href="/murmur/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/murmur/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a>

  </li>
  </ul>
</nav>




</div>
        
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
  </div>

  <aside class="sidebar">

    <div class="sidebar-inner sidebar-overview-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <section class="post-toc-wrap sidebar-panel">
        </section>
        <!--/noindex-->

        <section class="site-overview-wrap sidebar-panel">
          <div class="site-author site-overview-item animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">William Leung</p>
  <div class="site-description" itemprop="description"></div>
</div>
<div class="site-state-wrap site-overview-item animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/murmur/archives">
          <span class="site-state-item-count">43</span>
          <span class="site-state-item-name">文章</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/murmur/categories/">
          
        <span class="site-state-item-count">9</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/murmur/tags/">
        <span class="site-state-item-count">1</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>



        </section>
      </div>
    </div>
  </aside>
  <div class="sidebar-dimmer"></div>


    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


    <div class="main-inner index posts-expand">
      

      
    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://gitee.com/lianghuilin/murmur.git/interview_questions/css/CSS%E4%BC%AA%E7%B1%BB%E5%92%8C%E4%BC%AA%E5%85%83%E7%B4%A0%E9%80%89%E6%8B%A9%E5%99%A8%E7%9A%84%E5%8C%BA%E5%88%AB/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/murmur/images/avatar.gif">
      <meta itemprop="name" content="William Leung">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="汇话板">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/murmur/interview_questions/css/CSS%E4%BC%AA%E7%B1%BB%E5%92%8C%E4%BC%AA%E5%85%83%E7%B4%A0%E9%80%89%E6%8B%A9%E5%99%A8%E7%9A%84%E5%8C%BA%E5%88%AB/" class="post-title-link" itemprop="url">CSS伪类和伪元素的区别</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-03-01 23:00:12" itemprop="dateCreated datePublished" datetime="2021-03-01T23:00:12+08:00">2021-03-01</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">更新于</span>
        <time title="修改时间：2021-11-28 19:02:23" itemprop="dateModified" datetime="2021-11-28T19:02:23+08:00">2021-11-28</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/murmur/categories/%E7%81%AB%E6%98%9F%E8%AE%A1%E5%88%92/" itemprop="url" rel="index"><span itemprop="name">火星计划</span></a>
        </span>
    </span>

  
    <span class="post-meta-item" title="本文字数">
      <span class="post-meta-item-icon">
        <i class="far fa-file-word"></i>
      </span>
      <span class="post-meta-item-text">本文字数：</span>
      <span>316</span>
    </span>
    <span class="post-meta-item" title="阅读时长">
      <span class="post-meta-item-icon">
        <i class="far fa-clock"></i>
      </span>
      <span class="post-meta-item-text">阅读时长 &asymp;</span>
      <span>1 分钟</span>
    </span>
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <p>从设计初衷去理解CSS伪类和伪元素</p>
          <!--noindex-->
            <div class="post-button">
              <a class="btn" href="/murmur/interview_questions/css/CSS%E4%BC%AA%E7%B1%BB%E5%92%8C%E4%BC%AA%E5%85%83%E7%B4%A0%E9%80%89%E6%8B%A9%E5%99%A8%E7%9A%84%E5%8C%BA%E5%88%AB/">
                阅读全文 &raquo;
              </a>
            </div>
          <!--/noindex-->
        
      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://gitee.com/lianghuilin/murmur.git/interview_questions/js/new%E4%B8%80%E4%B8%AAJS%E5%AF%B9%E8%B1%A1%E7%BB%8F%E5%8E%86%E4%BA%86%E4%BB%80%E4%B9%88/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/murmur/images/avatar.gif">
      <meta itemprop="name" content="William Leung">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="汇话板">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/murmur/interview_questions/js/new%E4%B8%80%E4%B8%AAJS%E5%AF%B9%E8%B1%A1%E7%BB%8F%E5%8E%86%E4%BA%86%E4%BB%80%E4%B9%88/" class="post-title-link" itemprop="url">new一个JS对象经历了什么</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-02-22 21:40:12" itemprop="dateCreated datePublished" datetime="2021-02-22T21:40:12+08:00">2021-02-22</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">更新于</span>
        <time title="修改时间：2021-11-28 19:03:46" itemprop="dateModified" datetime="2021-11-28T19:03:46+08:00">2021-11-28</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/murmur/categories/%E7%81%AB%E6%98%9F%E8%AE%A1%E5%88%92/" itemprop="url" rel="index"><span itemprop="name">火星计划</span></a>
        </span>
    </span>

  
    <span class="post-meta-item" title="本文字数">
      <span class="post-meta-item-icon">
        <i class="far fa-file-word"></i>
      </span>
      <span class="post-meta-item-text">本文字数：</span>
      <span>771</span>
    </span>
    <span class="post-meta-item" title="阅读时长">
      <span class="post-meta-item-icon">
        <i class="far fa-clock"></i>
      </span>
      <span class="post-meta-item-text">阅读时长 &asymp;</span>
      <span>1 分钟</span>
    </span>
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <p>JavaScript实例化对象的本质，基于面向原型的继承方案</p>
          <!--noindex-->
            <div class="post-button">
              <a class="btn" href="/murmur/interview_questions/js/new%E4%B8%80%E4%B8%AAJS%E5%AF%B9%E8%B1%A1%E7%BB%8F%E5%8E%86%E4%BA%86%E4%BB%80%E4%B9%88/">
                阅读全文 &raquo;
              </a>
            </div>
          <!--/noindex-->
        
      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://gitee.com/lianghuilin/murmur.git/reading_notes/%E3%80%8A%E4%BD%A0%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84JavaScript-%E4%B8%8A-%E3%80%8BS02E06/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/murmur/images/avatar.gif">
      <meta itemprop="name" content="William Leung">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="汇话板">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/murmur/reading_notes/%E3%80%8A%E4%BD%A0%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84JavaScript-%E4%B8%8A-%E3%80%8BS02E06/" class="post-title-link" itemprop="url">《你不知道的JavaScript(上)》S02E06</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>
      

      <time title="创建时间：2020-12-01 22:31:05 / 修改时间：23:07:24" itemprop="dateCreated datePublished" datetime="2020-12-01T22:31:05+08:00">2020-12-01</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/murmur/categories/%E4%B8%80%E5%91%A8%E4%B8%80%E7%AB%A0%E5%89%8D%E7%AB%AF%E4%B9%A6/" itemprop="url" rel="index"><span itemprop="name">一周一章前端书</span></a>
        </span>
    </span>

  
    <span class="post-meta-item" title="本文字数">
      <span class="post-meta-item-icon">
        <i class="far fa-file-word"></i>
      </span>
      <span class="post-meta-item-text">本文字数：</span>
      <span>12k</span>
    </span>
    <span class="post-meta-item" title="阅读时长">
      <span class="post-meta-item-icon">
        <i class="far fa-clock"></i>
      </span>
      <span class="post-meta-item-text">阅读时长 &asymp;</span>
      <span>11 分钟</span>
    </span>
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="第6章：-行为委托"><a href="#第6章：-行为委托" class="headerlink" title="第6章： 行为委托"></a>第6章： 行为委托</h1><ul>
<li>先简单回顾一下JavaScript的<code>[[Prototype]]</code>机制：<ul>
<li>JavaScript的<code>[[Prototype]</code>机制，本质上就是对象之间的关联关系。</li>
<li><code>[[Prototype]]</code>机制是指对象内部中包含另一个对象的引用，当第一个对象的属性访问不到时，引擎会通过引用，到<code>[[Prototype]]</code>关联的对象上继续查找，后者也没有找到，就会查找它的<code>[[Prototype]]</code>，以此类推，这一系列的对象被称为<code>原型链</code>。</li>
</ul>
</li>
<li>我们也说过，与其将JavaScript术语<code>[[Prototype]]</code>称为 <strong>原型继承</strong> ，不如叫 <strong>原型委托</strong> 更为准确。</li>
<li>而本章主要讲解就是“面向类”和“面向委托”设计模式之间的区别。</li>
</ul>
<h3 id="6-1-面向委托的设计"><a href="#6-1-面向委托的设计" class="headerlink" title="6.1 面向委托的设计"></a>6.1 面向委托的设计</h3><h5 id="6-1-1-类理论"><a href="#6-1-1-类理论" class="headerlink" title="6.1.1 类理论"></a>6.1.1 类理论</h5><ul>
<li>面向类的设计模式，通常先“抽象”父类的特征，然后用子类继承父类后进行特殊化。</li>
<li>举例说，用面向类的设计模式实现“汽车”和“飞机”：<ul>
<li>定义一个通用的父类<code>Transport</code>(运输工具)，Transport类定义公共的特性和行为；</li>
<li>接着定义子类<code>Car</code>(汽车)和<code>Aircraft</code>(飞机)，继承自<code>Transport</code>并且对自身的属性和行为进行特殊化；<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Transport</span> </span>&#123;</span><br><span class="line">    <span class="comment">//构造函数</span></span><br><span class="line">    Transport(id,passengerNum,name);  </span><br><span class="line">    </span><br><span class="line">    id;</span><br><span class="line">    passengerNum;   <span class="comment">//乘客数</span></span><br><span class="line">    name;         <span class="comment">//品牌名字</span></span><br><span class="line">    <span class="comment">//启动</span></span><br><span class="line">    <span class="function"><span class="title">launch</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">&#x27;载重人数：&#x27;</span>+passengerNum,<span class="string">&#x27;品牌名字：&#x27;</span>+name);</span><br><span class="line">    &#125;;    </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Car</span> <span class="title">inherits</span> <span class="title">Transport</span></span>&#123;</span><br><span class="line">    <span class="comment">//构造函数</span></span><br><span class="line">    <span class="function"><span class="title">Car</span>(<span class="params">id,passengerNum,name,wheelNum</span>)</span>&#123;</span><br><span class="line">        <span class="built_in">super</span>(id,passengerNum,name);</span><br><span class="line">        wheelNum = wheelNum;    </span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    wheelNum;  <span class="comment">//轮子数量</span></span><br><span class="line">    <span class="function"><span class="title">launch</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="built_in">super</span>();</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">&#x27;轮子数量：&#x27;</span> + wheelNum);</span><br><span class="line">    &#125;;   </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Aircraft</span> <span class="title">inherits</span> <span class="title">Transport</span></span>&#123;</span><br><span class="line">    <span class="comment">//构造函数</span></span><br><span class="line">    <span class="function"><span class="title">Aircraft</span>(<span class="params">id,passengerNum,name,wingNum</span>)</span>&#123;</span><br><span class="line">        <span class="built_in">super</span>(id,passengerNum,name);</span><br><span class="line">        wingNum = wingNum;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    wingNum;    <span class="comment">//机翼数量</span></span><br><span class="line">    <span class="function"><span class="title">launch</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="built_in">super</span>();</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">&#x27;机翼数量：&#x27;</span> + wingNum);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h5 id="6-1-2-委托理论"><a href="#6-1-2-委托理论" class="headerlink" title="6.1.2 委托理论"></a>6.1.2 委托理论</h5></li>
</ul>
</li>
<li>如果用面向委托的设计模式考虑同样的问题呢：<ul>
<li>同样需要定义<code>Transport</code>(运输工具)，但它只是包含公共的功能方法；</li>
<li>接着定义<code>Car</code>(汽车)和<code>Aircraft</code>(飞机)，存储具体的数据以及特殊方法；<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Transport</span> </span>&#123;</span><br><span class="line">    setId : <span class="function"><span class="keyword">function</span>(<span class="params">id</span>)</span>&#123;</span><br><span class="line">        <span class="built_in">this</span>.id = id;</span><br><span class="line">    &#125;,</span><br><span class="line">    setPassengerNum : <span class="function"><span class="keyword">function</span>(<span class="params">num</span>)</span>&#123;</span><br><span class="line">        <span class="built_in">this</span>.passengerNum = num;</span><br><span class="line">    &#125;,</span><br><span class="line">    setName : <span class="function"><span class="keyword">function</span>(<span class="params">name</span>)</span>&#123;</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="function"><span class="title">launch</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">&#x27;载重人数：&#x27;</span>+<span class="built_in">this</span>.passengerNum,<span class="string">&#x27;品牌名字：&#x27;</span>+<span class="built_in">this</span>.name);</span><br><span class="line">    &#125;,    </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//Car委托Transport</span></span><br><span class="line">Car = <span class="built_in">Object</span>.create(Transport);</span><br><span class="line">Car.prepareTransport = <span class="function"><span class="keyword">function</span>(<span class="params">id,passengerNum,name,wheelNum</span>)</span>&#123;</span><br><span class="line">    <span class="built_in">this</span>.setId(id);</span><br><span class="line">    <span class="built_in">this</span>.setPassengerNum(passengerNum);</span><br><span class="line">    <span class="built_in">this</span>.wheelNum = wheelNum;</span><br><span class="line">&#125;</span><br><span class="line">Car.prepareLaunch = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">this</span>.launch();</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">&#x27;轮子数量：&#x27;</span> + wheelNum);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

</li>
</ul>
</li>
</ul>
<h6 id="“面向类”和“面向委托”设计模式的区别"><a href="#“面向类”和“面向委托”设计模式的区别" class="headerlink" title="“面向类”和“面向委托”设计模式的区别"></a>“面向类”和“面向委托”设计模式的区别</h6><ul>
<li>数据存储在委托者(<code>Car</code>/<code>Aircraft</code>)上，而不是委托对象(<code>Transport</code>)上；</li>
<li>在面向类的设计模式中，父类和子类都拥有同名的方法<code>launch()</code>，但在面向委托的设计模式中，我们会尽量避免在<code>[[Prototype]]</code>链条的不同级别中，使用相同的命名；</li>
<li>如同<code>this.setId()</code>方法，由于调用位置触发了this的隐式绑定规则，虽然<code>setId()</code>方法在<code>Transport</code>中，运行时，仍然会绑定到<code>Car</code>。这说明，委托行为意味着<code>Car</code>对象在找不到属性或者方法引用时，会把这个请求委托给另一个对象<code>Transport</code>。</li>
</ul>
<blockquote>
<p>注意：在两个或两个以上互相委托的对象之间，创建循环委托是禁止的。 </p>
</blockquote>
<h5 id="6-1-3-比较思维模型"><a href="#6-1-3-比较思维模型" class="headerlink" title="6.1.3 比较思维模型"></a>6.1.3 比较思维模型</h5><ul>
<li>用伪代码，从理论上了解“面向类”和“面向委托”两种设计模式的区别后，我们从具体的JavaScript代码来比较两种设计模式的异同：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 交通工具</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;*&#125;</span> </span>id </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Transport</span>(<span class="params">id,name,passengerNum</span>)</span>&#123;</span><br><span class="line">    <span class="built_in">this</span>.id = id;</span><br><span class="line">    <span class="built_in">this</span>.name = name;</span><br><span class="line">    <span class="built_in">this</span>.passengerNum = passengerNum;</span><br><span class="line">&#125;</span><br><span class="line">Transport.prototype.launch = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">&#x27;载重人数：&#x27;</span>+<span class="built_in">this</span>.passengerNum,<span class="string">&#x27;品牌名字：&#x27;</span>+<span class="built_in">this</span>.name);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 汽车</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;*&#125;</span> </span>id </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;*&#125;</span> </span>name </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;*&#125;</span> </span>passengerNum </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Car</span>(<span class="params">id,name,passengerNum</span>)</span>&#123;</span><br><span class="line">    Transport.call(<span class="built_in">this</span>,id,name,passengerNum);</span><br><span class="line">&#125;</span><br><span class="line">Car.prototype = <span class="built_in">Object</span>.create(Transport.prototype);</span><br><span class="line">Car.prototype.run = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">this</span>.launch();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//实例化</span></span><br><span class="line"><span class="keyword">var</span> lexus = <span class="keyword">new</span> Car(<span class="number">1</span>,<span class="string">&#x27;雷克萨斯&#x27;</span>,<span class="number">8</span>);</span><br><span class="line"><span class="keyword">var</span> bmw = <span class="keyword">new</span> Car(<span class="number">2</span>,<span class="string">&#x27;宝马&#x27;</span>,<span class="number">8</span>);</span><br><span class="line"></span><br><span class="line">lexus.run();</span><br><span class="line">bmw.run();</span><br></pre></td></tr></table></figure></li>
<li>再用“面向委托”的设计模式，通过对象关联的代码风格来编写同样的代码：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 交通工具</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">Transport = &#123;</span><br><span class="line">    init : <span class="function"><span class="keyword">function</span>(<span class="params">id,name,passengerNum</span>)</span>&#123;</span><br><span class="line">        <span class="built_in">this</span>.id = id;</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">        <span class="built_in">this</span>.passengerNum = passengerNum;</span><br><span class="line">    &#125;,</span><br><span class="line">    launch : <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">&#x27;载重人数：&#x27;</span>+<span class="built_in">this</span>.passengerNum,<span class="string">&#x27;品牌名字：&#x27;</span>+<span class="built_in">this</span>.name);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 汽车</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">Car = <span class="built_in">Object</span>.create(Transport);</span><br><span class="line">Car.run = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">this</span>.launch();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//实例化</span></span><br><span class="line"><span class="keyword">var</span> mazda = <span class="built_in">Object</span>(Car);</span><br><span class="line">mazda.init(<span class="number">1</span>,<span class="string">&#x27;马自达&#x27;</span>,<span class="number">4</span>);</span><br><span class="line">mazda.run();</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> toyota = <span class="built_in">Object</span>(Car);</span><br><span class="line">toyota.init(<span class="number">2</span>,<span class="string">&#x27;丰田&#x27;</span>,<span class="number">4</span>);</span><br><span class="line">toyota.run();</span><br></pre></td></tr></table></figure></li>
<li>对比发现，代码简洁很多。很多时候我们要的只是将对象关联起来，并不需要那些既复杂又令人困惑的模仿类的行为，比如构造函数、原型以及<code>new</code>。</li>
</ul>
<h3 id="6-2-类与对象"><a href="#6-2-类与对象" class="headerlink" title="6.2 类与对象"></a>6.2 类与对象</h3><ul>
<li>接着我们通过真实的web前端案例来感受两种设计模式的不同，比如说我们要在页面上创建按钮控件。<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Widget父类</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;*&#125;</span> </span>width </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;*&#125;</span> </span>height </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Widget</span>(<span class="params">width,height</span>)</span>&#123;</span><br><span class="line">    <span class="built_in">this</span>.width = width;</span><br><span class="line">    <span class="built_in">this</span>.height = height;</span><br><span class="line">    <span class="built_in">this</span>.$elem = <span class="literal">null</span>;</span><br><span class="line">&#125;</span><br><span class="line">Widget.prototype.render = <span class="function"><span class="keyword">function</span>(<span class="params">$where</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">if</span>(<span class="built_in">this</span>.$elem)&#123;</span><br><span class="line">        <span class="built_in">this</span>.$elem.css(&#123;</span><br><span class="line">            width : <span class="built_in">this</span>.width + <span class="string">&#x27;px&#x27;</span>,</span><br><span class="line">            height: <span class="built_in">this</span>.height + <span class="string">&#x27;px&#x27;</span></span><br><span class="line">        &#125;).appendTo($where);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Button子类</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;*&#125;</span> </span>width </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;*&#125;</span> </span>height </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;*&#125;</span> </span>label </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Button</span>(<span class="params">width,height,label</span>)</span>&#123;</span><br><span class="line">    Widget.call(<span class="built_in">this</span>,width,height);</span><br><span class="line">    <span class="built_in">this</span>.label = label;</span><br><span class="line">&#125;</span><br><span class="line">Button.prototype = <span class="built_in">Object</span>.create(Widget.prototype);</span><br><span class="line"><span class="comment">//重写render方法</span></span><br><span class="line">Button.prototype.render = <span class="function"><span class="keyword">function</span>(<span class="params">$where</span>)</span>&#123;</span><br><span class="line">    <span class="comment">//super调用</span></span><br><span class="line">    Widget.prototype.render.call(<span class="built_in">this</span>,$where);</span><br><span class="line">    <span class="comment">//绑定事件</span></span><br><span class="line">    <span class="built_in">this</span>.$elem.click(<span class="built_in">this</span>.onClick.bind(<span class="built_in">this</span>));</span><br><span class="line">&#125;</span><br><span class="line">Button.prototype.onClick = <span class="function"><span class="keyword">function</span>(<span class="params">evt</span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">&#x27;Button&#x27;</span>+<span class="built_in">this</span>.label+<span class="string">&#x27; clicked!&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//调用</span></span><br><span class="line">$(<span class="built_in">document</span>).render(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">var</span> $body = $(<span class="built_in">document</span>.body);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">var</span> saveBtn = <span class="keyword">new</span> Button(<span class="number">125</span>,<span class="number">30</span>,<span class="string">&#x27;暂存&#x27;</span>);</span><br><span class="line">    <span class="keyword">var</span> submitBtn = <span class="keyword">new</span> Button(<span class="number">125</span>,<span class="number">30</span>,<span class="string">&#x27;提交&#x27;</span>);</span><br><span class="line"></span><br><span class="line">    saveBtn.render($body);</span><br><span class="line">    submitBtn.render($body);</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure></li>
<li>用面向委托的代码来更简单的实现Widget/Button：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Widget父类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">var</span> Widget = &#123;</span><br><span class="line">    init: <span class="function"><span class="keyword">function</span> (<span class="params">width, height</span>) </span>&#123;</span><br><span class="line">        <span class="built_in">this</span>.width = width || <span class="number">50</span>;</span><br><span class="line">        <span class="built_in">this</span>.height = height || <span class="number">50</span>;</span><br><span class="line">        <span class="built_in">this</span>.$elem = <span class="literal">null</span>;</span><br><span class="line">    &#125;,</span><br><span class="line">    insert: <span class="function"><span class="keyword">function</span> (<span class="params">$where</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">this</span>.$elem) &#123;</span><br><span class="line">            <span class="built_in">this</span>.$elem.css(&#123;</span><br><span class="line">                width: <span class="built_in">this</span>.width + <span class="string">&#x27;px&#x27;</span>,</span><br><span class="line">                height: <span class="built_in">this</span>.height + <span class="string">&#x27;px&#x27;</span></span><br><span class="line">            &#125;).appendTo($where);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Button子类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">var</span> Button = <span class="built_in">Object</span>.create(Widget);</span><br><span class="line">Button.setup = <span class="function"><span class="keyword">function</span>(<span class="params">width,height,label</span>)</span>&#123;</span><br><span class="line">    <span class="built_in">this</span>.init(width,height);</span><br><span class="line">    <span class="built_in">this</span>.label = label || <span class="string">&#x27;Default&#x27;</span>;</span><br><span class="line">    <span class="built_in">this</span>.$elem = $(<span class="string">&#x27;&lt;button&gt;&#x27;</span>).text(<span class="built_in">this</span>.label);</span><br><span class="line">&#125;</span><br><span class="line">Button.build = <span class="function"><span class="keyword">function</span>(<span class="params">$where</span>)</span>&#123;</span><br><span class="line">    <span class="built_in">this</span>.insert($where);</span><br><span class="line">    <span class="built_in">this</span>.$elem.click(<span class="built_in">this</span>.onClick.bind(<span class="built_in">this</span>));</span><br><span class="line">&#125;</span><br><span class="line">Button.onClick = <span class="function"><span class="keyword">function</span>(<span class="params">evt</span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">&#x27;Button&#x27;</span>+<span class="built_in">this</span>.label+<span class="string">&#x27; clicked!&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//调用</span></span><br><span class="line">$(<span class="built_in">document</span>).render(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">var</span> $body = $(<span class="built_in">document</span>.body);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">var</span> saveBtn = <span class="built_in">Object</span>.create(Button);</span><br><span class="line">    saveBtn.setup(<span class="number">125</span>,<span class="number">30</span>,<span class="string">&#x27;暂存&#x27;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">var</span> submitBtn = <span class="built_in">Object</span>.create(Button);</span><br><span class="line">    submitBtn.setup(<span class="number">125</span>,<span class="number">30</span>,<span class="string">&#x27;提交&#x27;</span>);</span><br><span class="line"></span><br><span class="line">    saveBtn.build($body);</span><br><span class="line">    submitBtn.build($body);</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>
<blockquote>
<p>注意：</p>
<ul>
<li>在面向委托的代码中，没有像类一样，定义相同的方法名<code>render()</code>，而是定义了两个更具描述性的方法名<code>insert()</code>和<code>build()</code>，在很多情况下，将构造和初始化步骤分开，更灵活；</li>
<li>同时在面向委托的代码中，我们避免丑陋的显式伪多态调用<code>Widget.call</code>和<code>Widget.prototype.render.call</code>，取而代之简单的相对委托调用<code>this.init()</code>和<code>this.insert()</code>；</li>
</ul>
</blockquote>
</li>
</ul>
<h3 id="6-3-更简洁的设计"><a href="#6-3-更简洁的设计" class="headerlink" title="6.3 更简洁的设计"></a>6.3 更简洁的设计</h3><ul>
<li>第三个例子，是分别用“面向类”和“面向委托”的风格代码来实现一个登陆界面。<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 父类Controller</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Controller</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">this</span>.errors = [];</span><br><span class="line">&#125;</span><br><span class="line">Controller.prototype.showDialog = <span class="function"><span class="keyword">function</span>(<span class="params">title,msg</span>)</span>&#123;</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">&#125;</span><br><span class="line">Controller.prototype.success = <span class="function"><span class="keyword">function</span>(<span class="params">msg</span>)</span>&#123;</span><br><span class="line">    <span class="built_in">this</span>.showDialog(<span class="string">&#x27;Success&#x27;</span>,msg);</span><br><span class="line">&#125;</span><br><span class="line">Controller.prototype.failure = <span class="function"><span class="keyword">function</span>(<span class="params">err</span>)</span>&#123;</span><br><span class="line">    <span class="built_in">this</span>.errors.push(err);</span><br><span class="line">    <span class="built_in">this</span>.showDialog(<span class="string">&#x27;Error&#x27;</span>,err);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 登陆表单Controller子类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">LoginController</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    Controller.call(<span class="built_in">this</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//继承Controller</span></span><br><span class="line">LoginController.prototype = <span class="built_in">Object</span>.create(Controller.prototype);</span><br><span class="line"><span class="comment">//获取用户名</span></span><br><span class="line">LoginController.prototype.getUser = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> docuemnt.getElmementById(<span class="string">&#x27;username&#x27;</span>).value;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//获取密码</span></span><br><span class="line">LoginController.prototype.getPwd = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> docuemnt.getElmementById(<span class="string">&#x27;userPwd&#x27;</span>).value;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//表单校验</span></span><br><span class="line">LoginController.prototype.validate = <span class="function"><span class="keyword">function</span>(<span class="params">user,pwd</span>)</span>&#123;</span><br><span class="line">    user = user || <span class="built_in">this</span>.getUser();</span><br><span class="line">    pwd = pwd || <span class="built_in">this</span>.getPwd();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(!(user &amp;&amp; pwd))&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">this</span>.failure(<span class="string">&#x27;请输入用户名/密码!&#x27;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span>(pwd.length &lt; <span class="number">5</span>)&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">this</span>.failure(<span class="string">&#x27;密码长度不能少于五位!&#x27;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//重写failure</span></span><br><span class="line">LoginController.prototype.failure = <span class="function"><span class="keyword">function</span>(<span class="params">err</span>)</span>&#123;</span><br><span class="line">    Controller.prototype.failure.call(<span class="built_in">this</span>,<span class="string">&#x27;登陆失败：&#x27;</span>+err);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 授权检查Controller子类</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;*&#125;</span> </span>login </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">AuthController</span>(<span class="params">login</span>)</span>&#123;</span><br><span class="line">    Controller.call(<span class="built_in">this</span>);</span><br><span class="line">    <span class="built_in">this</span>.login = login;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//继承Controller</span></span><br><span class="line">AuthController.prototype = <span class="built_in">Object</span>.create(Controller.prototype);</span><br><span class="line"><span class="comment">//授权检查</span></span><br><span class="line">AuthController.prototype.checkAuth = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">var</span> user = <span class="built_in">this</span>.login.getUser();</span><br><span class="line">    <span class="keyword">var</span> pwd = <span class="built_in">this</span>.login.getPwd();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(<span class="built_in">this</span>.login.validate(user,pwd))&#123;</span><br><span class="line">        <span class="built_in">this</span>.send(<span class="string">&#x27;/check-auth&#x27;</span>,&#123;</span><br><span class="line">            user : user,</span><br><span class="line">            pwd : pwd</span><br><span class="line">        &#125;)</span><br><span class="line">        .then(<span class="built_in">this</span>.success.bind(<span class="built_in">this</span>))</span><br><span class="line">        .fail(<span class="built_in">this</span>.failure.bind(<span class="built_in">this</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//发送请求</span></span><br><span class="line">AuthController.prototype.send = <span class="function"><span class="title">functioin</span>(<span class="params">url,data</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> $.ajax(&#123;</span><br><span class="line">        url : url,</span><br><span class="line">        data : data</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//重写基础的success</span></span><br><span class="line">AuthController.prototype.success = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    Controller.prototype.success.call(<span class="built_in">this</span>,<span class="string">&#x27;授权检查成功!&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//重写基础的failure</span></span><br><span class="line">AuthController.prototype.failure = <span class="function"><span class="keyword">function</span>(<span class="params">err</span>)</span>&#123;</span><br><span class="line">    Controller.prototype.failure.call(<span class="built_in">this</span>,<span class="string">&#x27;授权检查失败：&#x27;</span>+err);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 调用</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="comment">//除了继承，还要合成</span></span><br><span class="line"><span class="keyword">var</span> auth = <span class="keyword">new</span> AuthController(<span class="keyword">new</span> LoginController());</span><br><span class="line">auth.checkAuth();</span><br></pre></td></tr></table></figure></li>
<li>用对象关联风格的行为委托来实现：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Login</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">var</span> LoginController = &#123;</span><br><span class="line">    errors : [],</span><br><span class="line">    getUser : <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> docuemnt.getElmementById(<span class="string">&#x27;username&#x27;</span>).value;</span><br><span class="line">    &#125;,</span><br><span class="line">    getPwd : <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> docuemnt.getElmementById(<span class="string">&#x27;userPwd&#x27;</span>).value;</span><br><span class="line">    &#125;,</span><br><span class="line">    validate : <span class="function"><span class="keyword">function</span>(<span class="params">user,pwd</span>)</span>&#123;</span><br><span class="line">        user = user || <span class="built_in">this</span>.getUser();</span><br><span class="line">        pwd = pwd || <span class="built_in">this</span>.getPwd();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span>(!(user &amp;&amp; pwd))&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="built_in">this</span>.failure(<span class="string">&#x27;请输入用户名/密码!&#x27;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span>(pwd.length &lt; <span class="number">5</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="built_in">this</span>.failure(<span class="string">&#x27;密码长度不能少于五位!&#x27;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;,</span><br><span class="line">    showDialog : <span class="function"><span class="keyword">function</span>(<span class="params">title,msg</span>)</span>&#123;</span><br><span class="line">        <span class="comment">//...</span></span><br><span class="line">    &#125;,</span><br><span class="line">    failure : <span class="function"><span class="keyword">function</span>(<span class="params">err</span>)</span>&#123;</span><br><span class="line">        <span class="built_in">this</span>.errors.push(err);</span><br><span class="line">        <span class="built_in">this</span>.showDialog(<span class="string">&#x27;Error&#x27;</span>,<span class="string">&#x27;登陆失败：&#x27;</span>+err);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Auth</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">var</span> AuthController = <span class="built_in">Object</span>.create(LoginController);</span><br><span class="line">AuthController.errors = [];</span><br><span class="line">AuthController.checkAuth = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">var</span> user = <span class="built_in">this</span>.login.getUser();</span><br><span class="line">    <span class="keyword">var</span> pwd = <span class="built_in">this</span>.login.getPwd();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(<span class="built_in">this</span>.login.validate(user,pwd))&#123;</span><br><span class="line">        <span class="built_in">this</span>.send(<span class="string">&#x27;/check-auth&#x27;</span>,&#123;</span><br><span class="line">            user : user,</span><br><span class="line">            pwd : pwd</span><br><span class="line">        &#125;)</span><br><span class="line">        .then(<span class="built_in">this</span>.success.bind(<span class="built_in">this</span>))</span><br><span class="line">        .fail(<span class="built_in">this</span>.failure.bind(<span class="built_in">this</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">AuthController.send = <span class="function"><span class="title">functioin</span>(<span class="params">url,data</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> $.ajax(&#123;</span><br><span class="line">        url : url,</span><br><span class="line">        data : data</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br><span class="line">AuthController.accepted = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">this</span>.showDialog(<span class="string">&#x27;系统提示&#x27;</span>,<span class="string">&#x27;授权检查成功!&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line">AuthController.rejected = <span class="function"><span class="keyword">function</span>(<span class="params">err</span>)</span>&#123;</span><br><span class="line">    <span class="built_in">this</span>.showDialog(<span class="string">&#x27;系统提示&#x27;</span>,<span class="string">&#x27;授权检查失败：&#x27;</span>+err)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 调用</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">var</span> loginCtrl = <span class="built_in">Object</span>.create(LoginController);</span><br><span class="line"><span class="keyword">var</span> authCtrl = <span class="built_in">Object</span>.create(AuthController);</span><br></pre></td></tr></table></figure></li>
<li>在行为委托模式中，<code>AuthController</code>和<code>LoginController</code>只是兄弟关系的对象，不是子类和父类的关系。相比起面向类的设计模式，只需要两个实体就可以了，也不需要实例化类。</li>
</ul>
<h3 id="6-4-更好的语法"><a href="#6-4-更好的语法" class="headerlink" title="6.4 更好的语法"></a>6.4 更好的语法</h3><ul>
<li>另外，ES6的<code>class</code>的语法可以简洁的定义类方法，不用<code>function</code>关键字：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> LoginController = &#123;</span><br><span class="line">    errors : [],</span><br><span class="line">    <span class="function"><span class="title">getUser</span>(<span class="params"></span>)</span> &#123;</span><br><span class="line">        <span class="comment">//可以不用function来定义</span></span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="function"><span class="title">getPwd</span>(<span class="params"></span>)</span> &#123;</span><br><span class="line">        <span class="comment">//...</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> AuthController = &#123;</span><br><span class="line">    errors : [],</span><br><span class="line">    <span class="function"><span class="title">checkAuth</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="comment">//...</span></span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="function"><span class="title">send</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="comment">//...</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//把AuthController关联到LoginController</span></span><br><span class="line"><span class="built_in">Object</span>.setPrototypeOf(AuthController,LoginController);</span><br></pre></td></tr></table></figure>

</li>
</ul>
<h3 id="6-5-内省"><a href="#6-5-内省" class="headerlink" title="6.5 内省"></a>6.5 内省</h3><ul>
<li>内省即检查实例的类型，在这个方面上，面向委托的代码也比面向类更方便。</li>
<li>前文提到，检测实例是否属于某个类对象，用<code>instance</code>：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//类</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Fruit</span>(<span class="params"></span>)</span>&#123;&#125;</span><br><span class="line">Fruit.prototype.something = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;&#125;</span><br><span class="line"><span class="comment">//实例</span></span><br><span class="line"><span class="keyword">var</span> fruit = <span class="keyword">new</span> Fruit();</span><br><span class="line"><span class="comment">//判断实例的类型，以便调用某个方法</span></span><br><span class="line"><span class="keyword">if</span>(fruit <span class="keyword">instanceof</span> Fruit)&#123;</span><br><span class="line">    fruit.something();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
<li>但如果是多继承关系的情况下，则必须借助<code>Object.isPrototypeOf()</code>和<code>Object.getPrototypeOf（）</code>等方法：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//水果</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Fruit</span>(<span class="params"></span>)</span>&#123;&#125;</span><br><span class="line">Fruit.prototype.something = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;&#125;</span><br><span class="line"><span class="comment">//苹果</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Apple</span>(<span class="params"></span>)</span>&#123;&#125;</span><br><span class="line">Apple.prototype = <span class="built_in">Object</span>.create(Fruit.prototype);</span><br><span class="line"><span class="comment">//苹果实例</span></span><br><span class="line"><span class="keyword">var</span> apple = <span class="keyword">new</span> Apple();</span><br><span class="line"></span><br><span class="line"><span class="comment">//检测结果均为true</span></span><br><span class="line"><span class="built_in">console</span>.log(Apple.prototype <span class="keyword">instanceof</span> Fruit);</span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">Object</span>.getPrototypeOf(Apple.prototype) === Fruit.prototype);</span><br><span class="line"><span class="built_in">console</span>.log(Fruit.prototype.isPrototypeOf(Apple.prototype));</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(apple <span class="keyword">instanceof</span> Apple);</span><br><span class="line"><span class="built_in">console</span>.log(apple <span class="keyword">instanceof</span> Fruit);</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">Object</span>.getPrototypeOf(apple) === Apple.prototype);</span><br><span class="line"><span class="built_in">console</span>.log(Apple.prototype.isPrototypeOf(apple));</span><br><span class="line"><span class="built_in">console</span>.log(Fruit.prototype.isPrototypeOf(apple));</span><br><span class="line"></span><br><span class="line"><span class="comment">//或者通过鸭子类型的方式来检查，只要方法存在即调用</span></span><br><span class="line"><span class="keyword">if</span>(apple.something)&#123;</span><br><span class="line">    apple.something();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
<li>而面向委托的代码检测起来则比较简单：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> Fruit = &#123;</span><br><span class="line">    something : <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;&#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">var</span> Apple = <span class="built_in">Object</span>.create(Fruit);</span><br><span class="line"><span class="keyword">var</span> apple = <span class="built_in">Object</span>.craate(Apple);</span><br><span class="line"></span><br><span class="line"><span class="comment">//检测结果均为true</span></span><br><span class="line"><span class="built_in">console</span>.log(Fruit.isPrototypeOf(Apple));</span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">Object</span>.getPrototypeOf(Apple) === Apple);</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(Fruit.isPrototypeOf(apple));</span><br><span class="line"><span class="built_in">console</span>.log(Apple.isPrototypeOf(apple));</span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">Object</span>.getPrototypeOf(apple) === Apple);</span><br></pre></td></tr></table></figure>

</li>
</ul>
<h3 id="6-6-小结"><a href="#6-6-小结" class="headerlink" title="6.6 小结"></a>6.6 小结</h3><ul>
<li>JavaScript是一门灵活的语言，既可以采用面向类和继承的设计模式，这也是大多数开发者习惯的代码组织方式，另外也可以采用行为委托的设计模式。</li>
<li>相对于“面向类”的设计模式把对象看成父子关系，行为委托的设计模式认为对象之间是相互委托的兄弟关系。</li>
<li>JavaScript的<code>[[Prototype]]</code>机制本质就是行为委托机制，所以当使用面向委托的设计模式来组织代码时，也让代码更简洁和清晰。</li>
</ul>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://gitee.com/lianghuilin/murmur.git/reading_notes/%E3%80%8A%E4%BD%A0%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84JavaScript-%E4%B8%8A-%E3%80%8BS02E05/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/murmur/images/avatar.gif">
      <meta itemprop="name" content="William Leung">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="汇话板">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/murmur/reading_notes/%E3%80%8A%E4%BD%A0%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84JavaScript-%E4%B8%8A-%E3%80%8BS02E05/" class="post-title-link" itemprop="url">《你不知道的JavaScript(上)》S02E05</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>
      

      <time title="创建时间：2020-12-01 22:28:40 / 修改时间：23:07:25" itemprop="dateCreated datePublished" datetime="2020-12-01T22:28:40+08:00">2020-12-01</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/murmur/categories/%E4%B8%80%E5%91%A8%E4%B8%80%E7%AB%A0%E5%89%8D%E7%AB%AF%E4%B9%A6/" itemprop="url" rel="index"><span itemprop="name">一周一章前端书</span></a>
        </span>
    </span>

  
    <span class="post-meta-item" title="本文字数">
      <span class="post-meta-item-icon">
        <i class="far fa-file-word"></i>
      </span>
      <span class="post-meta-item-text">本文字数：</span>
      <span>6.7k</span>
    </span>
    <span class="post-meta-item" title="阅读时长">
      <span class="post-meta-item-icon">
        <i class="far fa-clock"></i>
      </span>
      <span class="post-meta-item-text">阅读时长 &asymp;</span>
      <span>6 分钟</span>
    </span>
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="第5章-：原型"><a href="#第5章-：原型" class="headerlink" title="第5章 ：原型"></a>第5章 ：原型</h1><h3 id="5-1-Prototype"><a href="#5-1-Prototype" class="headerlink" title="5.1 [[Prototype]]"></a>5.1 [[Prototype]]</h3><ul>
<li>JavaScript的对象都有一个<code>[[Prototype]]</code>内置属性，它是一个对象的引用。对象在创建时<code>[[Prototype]]</code>属性就会被赋予值。<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//举例：创建一个myObj对象，然后读取其属性值</span></span><br><span class="line"><span class="keyword">var</span> myObj = &#123;</span><br><span class="line">    a : <span class="number">2</span></span><br><span class="line">&#125;;</span><br><span class="line">myObj.a;    <span class="comment">// 2</span></span><br></pre></td></tr></table></figure></li>
<li>之前说过，当访问对象的属性时，会触发<code>[[Get]]</code>操作。<code>[[Get]]</code>操作第一步是检查对象本身是否有这个属性，如果有就使用；但是如果没有这个属性，就需要使用对象的<code>[[Prototype]]</code>链了。<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> anotherObj = &#123;</span><br><span class="line">    a : <span class="number">2</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// Object.create()方法是根据传入的对象，创建一个新对象，并且原型链上关联到传入的对象</span></span><br><span class="line"><span class="keyword">var</span> myObj = <span class="built_in">Object</span>.create(anotherObj);</span><br><span class="line">myObj.a;  <span class="comment">// 2</span></span><br></pre></td></tr></table></figure></li>
<li>实际上，所有访问属性的操作，都会查询<code>[[Prototype]]</code>链，包括<code>for..in</code>遍历、<code>in</code>操作等。<h5 id="5-1-1-object-prototype"><a href="#5-1-1-object-prototype" class="headerlink" title="5.1.1 object.prototype"></a>5.1.1 object.prototype</h5></li>
<li>所有的<code>[[Prototype]]</code>的尽头都执行内置的<code>object.prototype</code>对象，导致所有的普通对象都包含<code>Object.prototype</code>对象的许多通用的功能，比如<code>toString()</code>、<code>valueOf()</code>、<code>hasOwnProperty()</code>、<code>isPrototypeOf()</code>等方法。<h5 id="5-1-2-属性设置和屏蔽"><a href="#5-1-2-属性设置和屏蔽" class="headerlink" title="5.1.2 属性设置和屏蔽"></a>5.1.2 属性设置和屏蔽</h5><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">myObj.foo = <span class="string">&#x27;bar&#x27;</span>;</span><br></pre></td></tr></table></figure></li>
<li>当通过.操作符访问对象进行属性赋值/属性追加时，有如下几种情况：<ul>
<li>如果当前对象中包含该属性，则修改当前对象的属性值（此时即便对象的原型链上包含同名的属性，根据就近原则，也会被当前对象的属性值屏蔽）</li>
<li>如果当前对象不包含该属性，就会遍历查找对象的原型链是否有该属性：<ul>
<li>如果在<code>[[Prototype]]</code>链上也不包含该属性，属性会被直接添加到当前对象<code>myObj</code>上；</li>
<li>如果在<code>[[Prototype]]</code>链上存在该属性：<ul>
<li>且属性非只读(<code>writable:false</code>)，那么在当前对象上添加一个名为<code>foo</code>的新属性，系统视它为继承了原型对象的屏蔽属性；</li>
<li>但属性是只读(<code>writable:true</code>)，那么将无法修改属性，也无法创建新属性。允许在严格模式下，代码会抛出一个错误；非严格模式下，这条赋值语句会被忽略；</li>
<li>且属性是个<code>setter</code>，那就一定会调用这个<code>setter</code>，属性的赋值屏蔽于<code>myObj</code></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>值得注意的是，<code>++</code>操作符会造成隐式屏蔽<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> anotherObj = &#123;</span><br><span class="line">    a : <span class="number">2</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//创建一个原型关联anotherObj的新对象</span></span><br><span class="line"><span class="keyword">var</span> myObj = <span class="built_in">Object</span>.create(anotherObject);</span><br><span class="line"><span class="comment">//不管子对象还是父对象，a属性值都输出2</span></span><br><span class="line">anotherObj.a;   <span class="comment">// 2</span></span><br><span class="line">myObjt.a;   <span class="comment">// 2</span></span><br><span class="line"><span class="comment">//父对象中拥有a，而子对象中不拥有a</span></span><br><span class="line">anotberObj.hasOwnProperty(<span class="string">&#x27;a&#x27;</span>); <span class="comment">// true</span></span><br><span class="line">myObj.hasOwnProperty(<span class="string">&#x27;a&#x27;</span>);  <span class="comment">//false</span></span><br><span class="line"><span class="comment">//执行++操作，造成隐式屏蔽</span></span><br><span class="line">myObj.a++;</span><br><span class="line"><span class="comment">//再看发现子类自己拥有了a属性，屏蔽了原型链上的同名属性</span></span><br><span class="line">anotherObj.a;   <span class="comment">// 2</span></span><br><span class="line">myObj.a;    <span class="comment">// 3</span></span><br><span class="line">myObj.hasOwnProperty(<span class="string">&#x27;a&#x27;</span>);  <span class="comment">// ture</span></span><br></pre></td></tr></table></figure>

</li>
</ul>
<h3 id="5-2-“类”"><a href="#5-2-“类”" class="headerlink" title="5.2 “类”"></a>5.2 “类”</h3><ul>
<li>如第4章提到的，JavaScript中没有类。面向类的语言需要通过类作为对象的蓝图来创建对象，而JavaScript是直接创建对象的。实际上JavaScript才是真正应该被称为“面向对象”的语言。<h5 id="5-2-1-“类”函数"><a href="#5-2-1-“类”函数" class="headerlink" title="5.2.1 “类”函数"></a>5.2.1 “类”函数</h5><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Foo</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> a = <span class="keyword">new</span> Foo();</span><br><span class="line"><span class="built_in">Object</span>.getPrototypeOf(a) === Foo.prototype; <span class="comment">// ture</span></span><br></pre></td></tr></table></figure></li>
<li>如上面代码所示，<code>new Foo()</code>会生成一个新对象<code>a</code>，新对象内部的<code>[[Prototype]]</code>链关联到了<code>Foo.prototype</code>原型对象。</li>
<li>这里可以对比其他面向类的语言中，类可以被实例化多次，就像用模具制作东西一样，而实例化就意味着“把类的行为复制到物理对象中”，每一个新实例都会重复这个过程。但JavaScript没有类似的复制机制，只是通过<code>[[Prototype]]</code>原型链将对象关联起来。<h6 id="关于名称"><a href="#关于名称" class="headerlink" title="关于名称"></a>关于名称</h6></li>
<li>这个通过<code>[[Prototype]]</code>将对象关联起来的机制被称为“ <strong>原型继承</strong> ”</li>
<li>但原型继承这个术语比较容易造成混淆，影响大家对JavaScript机制真实原理的理解。毕竟继承意味着复制操作，但JavaScript并不会复制对象属性。而是在对象之间穿件一个关联，对象通过委托访问另一个对象的属性和函数。 <strong>委托</strong> 这个术语更能准确的描述JavaScript中对象的关联机制。</li>
<li><strong>差异继承</strong> 就是指在描述对象行为时，不描述普遍的特质。比如，描述汽车时，我们更多会说“汽车是一个有四个轮子的交通工具”，但不会说“汽车是拥有引擎发动机(通用特质)的交通工具”</li>
</ul>
<h5 id="5-2-2-“构造函数”"><a href="#5-2-2-“构造函数”" class="headerlink" title="5.2.2 “构造函数”"></a>5.2.2 “构造函数”</h5><ul>
<li>之所以让我们认为<code>Foo</code>是一个“类”，一个原因是关键字<code>new</code>，在面向类的语言中构造类实例时也会用到<code>new</code>；另一个原因是，<code>Foo()</code>的调用看起来像是执行了类的构造函数方法。<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Foo</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br><span class="line">Foo.prototype.constructor === Foo;  <span class="comment">// true</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> a = <span class="keyword">new</span> Foo();</span><br><span class="line">a.constructor === Foo;  <span class="comment">// true</span></span><br></pre></td></tr></table></figure></li>
<li>可见<code>Foo.prototype.constructor</code>属性和<code>Foo</code>函数相等，创建的<code>a</code>对象的<code>constructor</code>属性，和<code>Foo</code>函数也相等。</li>
<li>凡此种种，我们很容易就误认为<code>Foo()</code>是一个构造函数。但实际上，<code>Foo()</code>本身并不是构造函数，和其他普通的函数没有任何区别。只是，在函数调用的前面加上<code>new</code>关键字后，变成一种“构造函数的调用方式”。<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">NothingSpecial</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">&quot;Don&#x27;t mind me&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> a = <span class="keyword">new</span> NothingSpecial(); <span class="comment">// Don&#x27;t mind me</span></span><br><span class="line">a; <span class="comment">// &#123;&#125;</span></span><br></pre></td></tr></table></figure></li>
<li>如上述代码所示，<code>NothingSpecial</code>只是一个普通的函数，使用<code>new</code>调用时，它会构造一个对象并赋值给变量<code>a</code>。</li>
<li>对于“构造函数”更准确的解释，应该是“带有new的函数调用”。</li>
</ul>
<h5 id="5-2-3-技术"><a href="#5-2-3-技术" class="headerlink" title="5.2.3 技术"></a>5.2.3 技术</h5><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//定义一个Foo函数，函数里将this.name属性重新赋值</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Foo</span>(<span class="params">name</span>)</span>&#123;</span><br><span class="line">    <span class="built_in">this</span>.name = name;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//为Foo.prototype原型对象定义一个myName()方法，返回name值</span></span><br><span class="line">Foo.prototype.myName = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">this</span>.name;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//通过new的方式调用Foo()函数，返回a、b两个对象</span></span><br><span class="line"><span class="keyword">var</span> a = <span class="keyword">new</span> Foo(<span class="string">&#x27;a&#x27;</span>);</span><br><span class="line"><span class="keyword">var</span> b = <span class="keyword">new</span> Foo(<span class="string">&#x27;b&#x27;</span>);</span><br><span class="line"><span class="comment">//调用a、b对象的myName()方法</span></span><br><span class="line">a.myName(); <span class="comment">// &#x27;a&#x27;</span></span><br><span class="line">b.myName(); <span class="comment">// &#x27;b&#x27;</span></span><br></pre></td></tr></table></figure>
<ul>
<li>看这段代码，调用了<code>new Foo()</code>后，创建的<code>a</code>和<code>b</code>对象都拥有了<code>myName()</code>方法。</li>
<li>看起来像是在创建<code>a</code>和<code>b</code>时，把<code>Foo.prototype</code>原型对象复制到两个对象中，但事实上并不是。</li>
<li>正如本章前文介绍<code>[[Get]]</code>算法时提到的，当访问对象的属性不存在时，会通过其的<code>[[[Prototype]]</code>原型链来查找。因此，在创建的过程中，<code>a</code>和<code>b</code>的<code>[[Prototype]]</code>原型对象会关联到<code>Foo.prototype</code>上。当访问<code>a.myName</code>时会通过原型链，委托关联到<code>Foo.prototype</code></li>
</ul>
<h6 id="回顾“构造函数”"><a href="#回顾“构造函数”" class="headerlink" title="回顾“构造函数”"></a>回顾“构造函数”</h6><ul>
<li><p>之前讨论<code>.constructor</code>属性时说过，虽然<code>a.constructor === Foo</code>为<code>true</code>，看起来<code>a</code>的构造函数(<code>constructor</code>)就是<code>Foo()</code>函数，但事实不是这样的。就如同<code>myName</code>属性一样，<code>a</code>对象本身并无<code>.constructor</code>属性，查找的其实是<code>Foo.prototype</code>原型对象的<code>myName</code>属性（也可以说，<code>a.constructor</code>被委托给了<code>Foo.prototype</code>）。而<code>Foo.prototype</code>本身的<code>.constructor</code>属性，是在<code>Foo</code>函数声明时的默认属性。</p>
</li>
<li><p>我们可以做个试验来验证这一点：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//定义Foo函数</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Foo</span>(<span class="params"></span>)</span>&#123;&#125;;</span><br><span class="line"><span class="comment">//将Foo的原型对象赋值为空</span></span><br><span class="line">Foo.prototype = &#123;&#125;;</span><br><span class="line"><span class="comment">//创建对象a</span></span><br><span class="line"><span class="keyword">var</span> a = <span class="keyword">new</span> Foo();</span><br><span class="line"><span class="comment">//测试</span></span><br><span class="line"><span class="built_in">console</span>.log(a.constructor === Foo);    <span class="comment">// false</span></span><br><span class="line"><span class="built_in">console</span>.log(a.constructor === <span class="built_in">Object</span>);  <span class="comment">// true </span></span><br></pre></td></tr></table></figure></li>
<li><p><code>a.constructor</code>属性本来委托给<code>Foo.prototype</code>，但我们已经把<code>Foo.prototype</code>重新赋值为空，所以她继续委托，直至最顶端的<code>Object.prototype</code>。</p>
</li>
<li><p>总之，对象的<code>.constructor</code>属性并不表示构造对象的函数。它只是一个不可变、且不可枚举，但可以被修改的属性，所以<code>.constructor</code>属性是不可靠且不安全的引用。</p>
<h3 id="5-3-原型继承"><a href="#5-3-原型继承" class="headerlink" title="5.3 原型继承"></a>5.3 原型继承</h3></li>
<li><p><code>Object.create()</code>方法会创建一个新对象，并把对象内部的<code>[[Prototype]]</code>关联到指定的对象，但缺点是通过创建一个新对象替代旧对象，而不是直接修改已有对象。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * ----------------------</span></span><br><span class="line"><span class="comment"> * 【示例：继承JS类并进行实例化】</span></span><br><span class="line"><span class="comment"> *  西瓜继承自水果，水果拥有type(食物类型)属性，西瓜拥有name(水果名称)属性</span></span><br><span class="line"><span class="comment"> * ----------------------</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="comment">//声明Fruit对象，并定义getType()方法</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Fruit</span>(<span class="params">type</span>)</span>&#123;</span><br><span class="line">    <span class="built_in">this</span>.type = type;</span><br><span class="line">&#125;</span><br><span class="line">Fruit.prototype.getType = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">this</span>.type;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//声明WateMelon对象，继承Fruit，并拥有getName()方法</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">WateMelon</span>(<span class="params">type,name</span>)</span>&#123;</span><br><span class="line">    Fruit.call(<span class="built_in">this</span>,type);</span><br><span class="line">    <span class="built_in">this</span>.name = name;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//通过Object.create()来继承</span></span><br><span class="line">WateMelon.prototype = <span class="built_in">Object</span>.create(Fruit.prototype);</span><br><span class="line">WateMelon.prototype.getName = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">this</span>.name;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//创建实例</span></span><br><span class="line"><span class="keyword">var</span> watemelon = <span class="keyword">new</span> WateMelon(<span class="string">&#x27;水果&#x27;</span>,<span class="string">&#x27;西瓜&#x27;</span>);</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">&#x27;食物品种：&#x27;</span>,watemelon.getType());</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">&#x27;食物名称：&#x27;</span>,watemelon.getName());</span><br><span class="line"></span><br></pre></td></tr></table></figure></li>
<li><p>想要直接修改对象的<code>[[Prototype]]</code>关联，在ES6之前，只能通过<code>.__proto__</code>属性来设置，但这个属性不能兼容所有浏览器，ES6添加了<code>Object.setPrototypeOf()</code>方法来实现。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Object</span>.setPrototypeOf(WateMelon.prototype,Fruit.prototype);</span><br></pre></td></tr></table></figure>
<h6 id="检查“类”关系"><a href="#检查“类”关系" class="headerlink" title="检查“类”关系"></a>检查“类”关系</h6></li>
<li><p>我们如何知道<code>watemelon</code>的委托对象是<code>WateMelon</code>呢？在传统的面向类环境中，检查实例对象的继承祖先通常被称为内省或者反射。</p>
</li>
<li><p>第一种方法是站在“类函数”的角度来判断，通过<code>instanceof</code>操作符判断实例对象是否属于某个类函数：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">watemelon <span class="keyword">instanceof</span> Watemelon; <span class="comment">// true</span></span><br><span class="line">watemelon <span class="keyword">instanceof</span> Fruit; <span class="comment">// true</span></span><br></pre></td></tr></table></figure></li>
<li><p><code>instanceof</code>操作符的左边是一个实例对象<code>watemelon</code>，右边是一个函数。<code>instanceof</code>会检查在<code>watemelon</code>的原型链中，是否有指向<code>Watemelon</code>或者<code>Fruit</code>的原型对象。</p>
<blockquote>
<p>注意：<code>instanceof</code> 判断的是实例对象和类的原型关联，而如果想判断两个实例对象之间是否有原型关联，则<code>instanceof</code>无法实现。</p>
</blockquote>
</li>
<li><p>第二种方法是通过<code>.isPrototypeOf()</code>方法，判断原型对象是否和指定的实例对象有关系：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">WateMelon.prototype.isPrototypeOf(watemelon);</span><br></pre></td></tr></table></figure></li>
<li><p><code>isPrototypeOf()</code>是基类<code>Object</code>的方法，判断watemelon的原型对象是不是Watemelon。</p>
<blockquote>
<p>注意：一定是要用原型对象调用<code>isPrototypeOf()</code>方法来做判断，如果用类函数(<code>WateMelon</code>)来调用，则返回<code>false</code>。</p>
</blockquote>
</li>
<li><p>除此之外，还可以通过<code>Object.getPrototypeOf(watemelon)</code>来获取实例对象的原型链。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Object</span>.getPrototypeOf(watemelon) === WateMelon.prototype;   <span class="comment">// true</span></span><br></pre></td></tr></table></figure>

</li>
</ul>
<h3 id="5-4-对象关联"><a href="#5-4-对象关联" class="headerlink" title="5.4 对象关联"></a>5.4 对象关联</h3><h5 id="5-4-1-创建关联"><a href="#5-4-1-创建关联" class="headerlink" title="5.4.1 创建关联"></a>5.4.1 创建关联</h5><ul>
<li>我们推荐用<code>create()</code>方法创建新对象，它可以充分发挥<code>[[Prototype]]</code>机制的威力，而是用new构造函数调用的方式，通过类来创建两个对象之间的关系，避免了一些不必要的麻烦，比如<code>.prototype</code>和<code>.constructor</code>属性引用的问题。</li>
<li>由于<code>Object.create()</code>方法是ES5新增的函数，如果想要在ES5之前的环境使用，可以通过简单的polyfill代码来处理：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(!<span class="built_in">Object</span>.create)&#123;</span><br><span class="line">    <span class="built_in">Object</span>.crate = <span class="function"><span class="keyword">function</span>(<span class="params">o</span>)</span>&#123;</span><br><span class="line">        <span class="comment">//通过一个临时的F类，建立指定原型关系</span></span><br><span class="line">        <span class="function"><span class="keyword">function</span> <span class="title">F</span>(<span class="params"></span>)</span>&#123;&#125;;</span><br><span class="line">        F.prototype = o;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> F();</span><br><span class="line">        </span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h5 id="5-4-2-关联关系是备用"><a href="#5-4-2-关联关系是备用" class="headerlink" title="5.4.2 关联关系是备用"></a>5.4.2 关联关系是备用</h5></li>
<li>通过<code>[[Prototype]]</code>原型机制可以为对象之间建立关联关系，处理当对象属性或方法“缺失”时，可以提供一个备用选项。<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = &#123;</span><br><span class="line">    echo : <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">&#x27;hello&#x27;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> b = <span class="built_in">Object</span>.create(a);</span><br><span class="line">b.echo();   <span class="comment">// &#x27;hello&#x27;</span></span><br></pre></td></tr></table></figure></li>
<li>但从某种程度上来看，代码却变得难以理解和维护，<code>b</code>明明没有定义<code>echo()</code>方法，这东西是哪来的？</li>
<li>当然，我们可以通过 <strong>内部委托</strong> 的方式让API的设计更加清晰：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = &#123;</span><br><span class="line">    echo : <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">&#x27;hello&#x27;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> b = <span class="built_in">Object</span>.create(a);</span><br><span class="line">b.doEcho = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">this</span>.echo();</span><br><span class="line">&#125;;</span><br><span class="line">b.doEcho();   <span class="comment">// &#x27;hello&#x27;</span></span><br></pre></td></tr></table></figure>


</li>
</ul>
<h3 id="5-5-小结"><a href="#5-5-小结" class="headerlink" title="5.5 小结"></a>5.5 小结</h3><ul>
<li>当访问对象中并不存在的属性时，<code>[[Get]]</code>操作会查找对象内部<code>[[Prototype]]</code>关联的对象。这个关联关系，实际上定义了一条“原型链”。</li>
<li>原型链的顶端是<code>Object.prototype</code>，<code>toString()</code>、<code>valueOf()</code>和其他通用的功能都挂载在该对象上。</li>
<li>关联两个对象最常用的方式是用<code>new</code>关键字进行函数调用，通常我们称为“构造函数调用”。</li>
<li>虽然JavaScript的<code>new</code>机制看起来，和传统面向类语言的“类初始化”和“类继承”很相似，但是有一个很重要的区别，就是不会进行复制，对象之间是通过内部的<code>[[Prototype]]</code>链关联的。</li>
<li>出于各种原因，相比起术语“原型继承”，“委托”更适合。因为对象之间的关系不是复制，而是委托。</li>
</ul>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://gitee.com/lianghuilin/murmur.git/reading_notes/%E3%80%8A%E4%BD%A0%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84JavaScript-%E4%B8%8A-%E3%80%8BS02E04/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/murmur/images/avatar.gif">
      <meta itemprop="name" content="William Leung">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="汇话板">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/murmur/reading_notes/%E3%80%8A%E4%BD%A0%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84JavaScript-%E4%B8%8A-%E3%80%8BS02E04/" class="post-title-link" itemprop="url">《你不知道的JavaScript(上)》S02E04</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>
      

      <time title="创建时间：2020-12-01 22:26:46 / 修改时间：23:07:14" itemprop="dateCreated datePublished" datetime="2020-12-01T22:26:46+08:00">2020-12-01</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/murmur/categories/%E4%B8%80%E5%91%A8%E4%B8%80%E7%AB%A0%E5%89%8D%E7%AB%AF%E4%B9%A6/" itemprop="url" rel="index"><span itemprop="name">一周一章前端书</span></a>
        </span>
    </span>

  
    <span class="post-meta-item" title="本文字数">
      <span class="post-meta-item-icon">
        <i class="far fa-file-word"></i>
      </span>
      <span class="post-meta-item-text">本文字数：</span>
      <span>4.7k</span>
    </span>
    <span class="post-meta-item" title="阅读时长">
      <span class="post-meta-item-icon">
        <i class="far fa-clock"></i>
      </span>
      <span class="post-meta-item-text">阅读时长 &asymp;</span>
      <span>4 分钟</span>
    </span>
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="第4章-：混合对象“类”"><a href="#第4章-：混合对象“类”" class="headerlink" title="第4章 ：混合对象“类”"></a>第4章 ：混合对象“类”</h1><ul>
<li>本章要介绍和类相关的面相对象编程的知识。首先会介绍面相对象类的设计模式：实例化(instantiation)、继承(inheritance)、多态(polymorphism)。</li>
<li>但由于这些概念上无法直接对应到JavaScript的对象机制，因此很多JavaScript开发者使用了一些比如mixin等解决方法来实现。<h3 id="4-1-类理论"><a href="#4-1-类理论" class="headerlink" title="4.1 类理论"></a>4.1 类理论</h3></li>
<li>类/继承，或者说面向对象，是用来描述代码的一种组织结构形式，它是一种软件对真实世界的建模方法。</li>
<li>面向对象编程强调的是数据和操作数据的行为，本质是是相互关联的。因此，面相对象编程推崇的是，把数据以及和它相关的行为封装起来。这在正式的计算机科学中被称为数据结构。</li>
<li>举例：我们在编程中要描述一个数据，然后我们会定义一个变量，这个值是一个字符串，这也就是数据。但我们在使用的过程之，往往关心的不是数据是什么，而是可以对数据做什么，也就是应用再数据上的行为（比如计算数据长度啊、追加数据啊、搜索数据中的字符啊等等）。因此这些行为被设计成了String类。</li>
<li>再来看一个例子：“汽车”可以被看做“交通工具”的一种，对于这种关系，我们在软件中可以定义Vehicle类和Car类来进行建模。</li>
<li>Vehicle类的定义，可能包含推进器(比如引擎)、载人能力等。它包含所有类型的交通工具，不管是飞机、汽车或者火车。</li>
<li>而Car类继承了Vehicle类，以通用的Vehicle类作为基础进行特殊化定义，比如加上汽车的车辆识别码等。</li>
<li>虽然Vehicle和Car会定义相同的方法，但实例中的数据可能是不同的，这就是类、继承和实例化。</li>
<li>类的另一个核心概念是多态，是说父类的通用行为可以被子类用更特殊的行为进行重写。</li>
</ul>
<h5 id="4-1-1-“类”设计模式"><a href="#4-1-1-“类”设计模式" class="headerlink" title="4.1.1 “类”设计模式"></a>4.1.1 “类”设计模式</h5><ul>
<li>有人可能从来没把类作为设计模式来看，反而更熟悉的是比如观察者模式、工厂模式、单例模式等其他模式。但是这些高级设计模式，都以面向对象类的基础上实现的。</li>
<li>可能你还听过“过程化编程”，这种代码只包含过程调用，没有高层的抽象。</li>
<li>除此之外，如果有函数式编程的开发经验，会知道类也是非常常用的设计模式，但类不是必须的编程基础，而是一种可选的代码抽象。而在其他编程语言比如Java中，并不给你选择的机会，万物皆对象。</li>
</ul>
<h5 id="4-1-2-JavaScript中的“类”"><a href="#4-1-2-JavaScript中的“类”" class="headerlink" title="4.1.2 JavaScript中的“类”"></a>4.1.2 JavaScript中的“类”</h5><ul>
<li>在软件设计中，类是一种可选的模式。由于许多开发者都非常喜欢面向类的软件设计，JavaScript提供了语法糖来满足对于类设计模式的最普片需求，比如ES6新增了比如class关键字等，但javascript中的“类”和和其他语言中的类并不一样。</li>
</ul>
<h3 id="4-2-类的机制"><a href="#4-2-类的机制" class="headerlink" title="4.2 类的机制"></a>4.2 类的机制</h3><ul>
<li>在许多面向类的语言中，内置会提供Stack类，这是一种“栈”数据结构，同时会提供一些公有的方法。但实际上我们并不是直接操作Stack，Stack类仅仅是一个抽象的表示，它本身并不是一个“栈”。使用的时候，通常也需要先实例化Stack类，然后才能对它进行操作。<h5 id="4-2-1-建造"><a href="#4-2-1-建造" class="headerlink" title="4.2.1 建造"></a>4.2.1 建造</h5></li>
<li>类和实例的概念源于房屋建造。<ol>
<li>在建造之前，建筑师通常会规划一个建造蓝图来描绘建筑的特性：宽、高、几室几厅、多少个窗户以及窗户的位置等。但在这个阶段，并不关心建筑会被建在哪，也不关心会建造多少个这样的建筑，甚至不用关心建筑中的内容（比如家具、壁纸、吊灯等）。建筑蓝图只是建筑计划，它们并不是真正的建筑。</li>
<li>接下来，需要一个建筑工人按照蓝图建造建筑，把规划好的特性从蓝图中复制到现实世界的建筑中。</li>
<li>完成后，建筑就成为了蓝图的物理实例。如果需要建筑多套房子，只需把工作都重复一遍，再创建一份副本。</li>
</ol>
</li>
<li>建筑和蓝图之间的关系是间接的。蓝图只是抽象描绘了建筑的结构，比如蓝图只表示门在哪，但并不是真正的门，如果想打开一扇门，那就必须接触真实的建筑才行。</li>
<li>一个类就是一张建造蓝图，为了获得真正的建筑，我们必须按照类来建造(实例化)一个东西，而这个东西通常被称为实例。</li>
</ul>
<h5 id="4-2-2-构造函数"><a href="#4-2-2-构造函数" class="headerlink" title="4.2.2 构造函数"></a>4.2.2 构造函数</h5><ul>
<li>实例是由一个特殊的类方法构造的，这个类方法的方法名通常和类名相同，称为构造函数。</li>
<li>构造函数大多情况下，需要用<code>new</code>来调用，构造函数的主要任务，就是初始化实例需要的所有信息。</li>
</ul>
<h3 id="4-3-类的继承"><a href="#4-3-类的继承" class="headerlink" title="4.3 类的继承"></a>4.3 类的继承</h3><ul>
<li>在面向类的语言中，当一个类继承另一个类时。后者通常被称为“子类”，前者会被称为“父类”。</li>
<li>从术语来看，显然是类比父母和孩子。在现实中，孩子会从父母继承许多基因特性，但通常孩子不会和父母一模一样，即便外貌长相会类似，但性格行为却大不相同，因为孩子是一个独一无二的存在。</li>
<li>同理，在程序中，相对于父类来说，子类也是一个独立个体。子类会包含父类行为的原始副本，但也可以重写所有继承的行为甚至定义新的行为。<blockquote>
<p>注意：有必要说明一下，这里说的父类和子类不是实例，而是像上文说得建造蓝图一样。我们应当把父类和子类称为父类DNA和子类DNA，需要根据这些DNA来创建(实例化)一个人，我们才拥有一个真实的实例。</p>
</blockquote>
</li>
</ul>
<h5 id="4-3-1-多态"><a href="#4-3-1-多态" class="headerlink" title="4.3.1 多态"></a>4.3.1 多态</h5><ul>
<li>当子类继承父类，引用父类原始的方法时，这里被称为多态（相对多态）。<blockquote>
<p>说明：实际上多态是任何方法都能引用继承层级中上层的类的方法。之所以说是“相对”，是因为当前子类引用的是父类的方法，而实际上还可以引用祖先类(superclass)的方法。</p>
</blockquote>
</li>
<li>多态并不表示子类和父类有关联，子类得到的只是父类的一个副本。类的继承其实就是复制。</li>
</ul>
<h5 id="4-3-2-多重继承"><a href="#4-3-2-多重继承" class="headerlink" title="4.3.2 多重继承"></a>4.3.2 多重继承</h5><ul>
<li>有些面向类的语言允许子类继承多个“父类”，多重继承意味着所有父类的定义都会被复制到子类中。</li>
<li>从表面看，多继承可以把许多功能组合在一起。然而这个机制也会带来很多复杂的问题。比如，当子类调用两个父类同名的方法时，就不知道该怎么处理了。</li>
<li>JavaScript本身也不提供“多重继承”功能，但也可以用各种办法来实现多重继承。</li>
</ul>
<h3 id="4-4-混入"><a href="#4-4-混入" class="headerlink" title="4.4 混入"></a>4.4 混入</h3><ul>
<li>在继承或实例化时，JavaScript的对象机制并不会自动执行复制行为。换句话说，JavaScript中只有对象，不存在可以被实例化的“类”。不会复制对象，它们只是被关联起来。</li>
<li>由于在其他语言中，类表现出来的都是复制行为，因此JavaScript开发者也想出了一个方法来模拟类的复制行为，就是混入。</li>
<li>有两种类型的混入：显式和隐式。<h5 id="4-4-1-显式混入"><a href="#4-4-1-显式混入" class="headerlink" title="4.4.1 显式混入"></a>4.4.1 显式混入</h5></li>
<li>之前提到的Vehicle和Car。接下来，我们用JavaScript手动实现复制功能。这个功能在许多库和框架中被称为<code>extend()</code>，但是为了方便理解我们称之为<code>minxin()</code>：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">minxin</span>(<span class="params">sourceOBj,targetObj</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">var</span> key <span class="keyword">in</span> sourceObj)&#123;</span><br><span class="line">        <span class="keyword">if</span>(!(key <span class="keyword">in</span> targetObj))&#123;</span><br><span class="line">            targetObj[key] = sourceObj[key];</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> targetObj;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> Vehicle = &#123;</span><br><span class="line">    engines : <span class="number">1</span>,</span><br><span class="line">    ignition: <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">&#x27;Turning on my engine.&#x27;</span>);</span><br><span class="line">    &#125;,</span><br><span class="line">    drive : <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="built_in">this</span>.ignition();</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">&#x27;Steering and moving forward!&#x27;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">var</span> Car = mixin(Vehicle,&#123;</span><br><span class="line">    wheels : <span class="number">4</span>,</span><br><span class="line">    drive : <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        Vehicle.drive.call(<span class="built_in">this</span>);</span><br><span class="line">        <span class="built_in">console</span>.log(</span><br><span class="line">            <span class="string">&#x27;Rolling on all&#x27;</span> + <span class="built_in">this</span>.wheels + <span class="string">&#x27; wheels!&#x27;</span></span><br><span class="line">        );</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></li>
<li>Car中就拥有一份Vehicle属性和函数的副本了，但注意：复制的是函数的引用。</li>
<li>再说多态：<code>Vehicle.drive.call(this)</code>就是显示多态，清楚证明调用的是父类还是祖先类；<code>inherited:drive()</code>就是相对多态。</li>
<li>mixin()的工作原理：它会遍历sourceObj的属性，如果在targetObj没有这个属性就会进行复制。</li>
<li>显式混入模式的另一种变体被称为“寄生继承”：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Vehicle</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">this</span>.engines = <span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line">Vehicle.prototype.ignition = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">&#x27;Turning on my engine.&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line">Vehicle.prototype.drive = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">this</span>.ignition();</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">&#x27;Steering and moving forward!&#x27;</span>);</span><br><span class="line">&#125;;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Car</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">var</span> car = <span class="keyword">new</span> Vehicle();</span><br><span class="line">    car.wheels = <span class="number">4</span>;</span><br><span class="line">    <span class="keyword">var</span> vehDrive = car.drive;</span><br><span class="line">    car.drive = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        vehDrive.call(<span class="built_in">this</span>);</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">&#x27;Rolling on all &#x27;</span> + <span class="built_in">this</span>.wheels + <span class="string">&#x27; wheels!&#x27;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> car;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> myCar = <span class="keyword">new</span> Car();</span><br><span class="line">myCar.drive();</span><br></pre></td></tr></table></figure>
<h5 id="4-4-2-隐式混入"><a href="#4-4-2-隐式混入" class="headerlink" title="4.4.2 隐式混入"></a>4.4.2 隐式混入</h5></li>
<li>隐式混入和显式伪多态很像，但同样拥有同样的问题：不是真正的对象复制，而是复制引用。<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> Something = &#123;</span><br><span class="line">    cool : <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="built_in">this</span>.greeting = <span class="string">&#x27;Hello World!&#x27;</span></span><br><span class="line">        <span class="built_in">this</span>.count = <span class="built_in">this</span>.count ? <span class="built_in">this</span>.count + <span class="number">1</span> : <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line">Something.cool();</span><br><span class="line">Something.greeting; <span class="comment">//&#x27;Hello World&#x27;</span></span><br><span class="line">Something.count;    <span class="comment">//1</span></span><br><span class="line">Another = &#123;</span><br><span class="line">    cool : <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        Something.cool.call(<span class="built_in">this</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line">Another.cool();</span><br><span class="line">Another.greeting;   <span class="comment">// &#x27;Hello World&#x27;</span></span><br><span class="line">Another.count;  <span class="comment">// 1</span></span><br></pre></td></tr></table></figure></li>
<li>如果在构造函数调用使用<code>Something.cool.call(this)</code>，最终的结果是<code>Something.cool()</code>中的赋值操作都会应用再Another对象上而不是Something对象上。</li>
<li>通常来说，应该尽量避免使用这一的结构，以保证代码的整洁和可维护性。</li>
</ul>
<h3 id="4-5-小结"><a href="#4-5-小结" class="headerlink" title="4.5 小结"></a>4.5 小结</h3><ul>
<li>类是一种设计模式，许多语言提供了面向类软件设计的原生语法。JavaScript也有类似的语法，但和其他语言中的类完全不同。</li>
<li>类意味着复制。传统的类被实例化的时候，它的行为会被复制到实例中。类被继承时，行为也会被复制到子类中。</li>
<li>多态看起来似乎是从子类引用父类，但本质上引用的其实是复制的结果。</li>
<li>JavaScript不会像类那样自动创建对象的副本。但通过混入模式可以模拟类的复制行为，但通常会产生丑陋且脆弱的语法，这会让代码更加难懂并且难以维护。</li>
<li>显式混入实际上无法完全模拟类的复制行为，因为对象只是复制引用，无法复制被引用的对象或者函数本身。</li>
<li>总的来说，在JavaScript中模拟类是得不偿失的，可能会买下更多隐患。</li>
</ul>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://gitee.com/lianghuilin/murmur.git/reading_notes/%E3%80%8A%E4%BD%A0%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84JavaScript-%E4%B8%8A-%E3%80%8BS02E03/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/murmur/images/avatar.gif">
      <meta itemprop="name" content="William Leung">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="汇话板">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/murmur/reading_notes/%E3%80%8A%E4%BD%A0%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84JavaScript-%E4%B8%8A-%E3%80%8BS02E03/" class="post-title-link" itemprop="url">《你不知道的JavaScript(上)》S02E03</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>
      

      <time title="创建时间：2020-12-01 22:24:10 / 修改时间：23:07:13" itemprop="dateCreated datePublished" datetime="2020-12-01T22:24:10+08:00">2020-12-01</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/murmur/categories/%E4%B8%80%E5%91%A8%E4%B8%80%E7%AB%A0%E5%89%8D%E7%AB%AF%E4%B9%A6/" itemprop="url" rel="index"><span itemprop="name">一周一章前端书</span></a>
        </span>
    </span>

  
    <span class="post-meta-item" title="本文字数">
      <span class="post-meta-item-icon">
        <i class="far fa-file-word"></i>
      </span>
      <span class="post-meta-item-text">本文字数：</span>
      <span>6.8k</span>
    </span>
    <span class="post-meta-item" title="阅读时长">
      <span class="post-meta-item-icon">
        <i class="far fa-clock"></i>
      </span>
      <span class="post-meta-item-text">阅读时长 &asymp;</span>
      <span>6 分钟</span>
    </span>
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="第3章：-对象"><a href="#第3章：-对象" class="headerlink" title="第3章： 对象"></a>第3章： 对象</h1><h3 id="3-1-语法"><a href="#3-1-语法" class="headerlink" title="3.1 语法"></a>3.1 语法</h3><ul>
<li>对象有两种方式定义：<ul>
<li>字面量的定义方式 <code>var obj = &#123; &#125;</code></li>
<li>构造函数的定义方式 <code>var obj = new Object();</code></li>
</ul>
</li>
<li>构造函数和字面量的定义所生成的对象是一样的，两者的区别是：字面量可以添加多个键/值对，但构造函数的定义如果想定义多个键/值对，则必须逐个添加属性。</li>
</ul>
<h3 id="3-2-类型"><a href="#3-2-类型" class="headerlink" title="3.2 类型"></a>3.2 类型</h3><ul>
<li>JavaScript一共有6种主要类型：<code>string</code>、<code>number</code>、<code>boolean</code>、<code>null</code>、<code>underfined</code>、<code>object</code>。但简单基本类型本身并不是对象。</li>
<li>实际上，JavaScript种有许多特殊的对象子类型，我们称之为复杂基本类型。比如函数，就是对象的一个子类型，它像普通对象一样但只可以调用，可以作为参数进行传递。除此之外，数组也是特殊的子对象类型。</li>
</ul>
<h5 id="内置对象"><a href="#内置对象" class="headerlink" title="内置对象"></a>内置对象</h5><ul>
<li>JavaScript种还有一些对象子类型，被称为内置对象：<code>String</code>、<code>Number</code>、<code>Boolean</code>、<code>Object</code>、<code>Function</code>、<code>Array</code>、<code>Date</code>、<code>RegExp</code>、<code>Error</code>。</li>
<li>这些内置对象表面上很像传统语音中的类型(type)或者类(class)，比如Java中的String类。但在JavaScript中，它们只是一些内置函数。可以通过这些内置函数通过构造函数的方式构造一个对应子类型的新对象。<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> str = <span class="string">&#x27;i am a string&#x27;</span>;</span><br><span class="line"><span class="keyword">typeof</span> str; <span class="comment">//string</span></span><br><span class="line">str <span class="keyword">instanceof</span> <span class="built_in">String</span>;  <span class="comment">// false</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> strObj = <span class="keyword">new</span> <span class="built_in">String</span>(<span class="string">&#x27;i am a string&#x27;</span>);</span><br><span class="line"><span class="keyword">typeof</span> strObj;  <span class="comment">// object</span></span><br><span class="line">strObj <span class="keyword">instanceof</span> <span class="built_in">String</span>;   <span class="comment">// true</span></span><br></pre></td></tr></table></figure></li>
<li>原始值’i am a string’并不是一个对象，只是一个字面量，且是不可改变的值。当对这个字面量上执行一些操作的时候，比如获取长度时，JavaScript会自动把字符串字面量转换成一个String对象，而不需要显式的创建一个对象。同样的，数值和布尔字面量也会自动转成对象。</li>
<li>null和undefined只要字面量形式，而没有对应的构造形式。相反，Date只有构造形式，而没有字面量形式。</li>
<li>对于Object、Array、Function和RegExp来说，无论使用文字还是构造形式，都是对象不是字面量。因为相比起字面量创建对象，构造形式可以传入额外的参数。</li>
<li>Error对象一般很少在代码中显式创建，只是在抛出异常时被自动创建。</li>
</ul>
<h3 id="3-3-内容"><a href="#3-3-内容" class="headerlink" title="3.3 内容"></a>3.3 内容</h3><ul>
<li>对象的内容是由任意类型的值组成的，我们称之为属性。</li>
<li>但这只是它的表现形式。在引擎内部，这些值的存储方式是多种多样的，一般不会直接存在对象容器内部。存的只是名称，就像指针一样，指向真正存储的位置。<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> myObj = &#123;</span><br><span class="line">    a : <span class="number">2</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">myObj.a; <span class="comment">// 2</span></span><br><span class="line">myObj[<span class="string">&#x27;a&#x27;</span>]; <span class="comment">// 2</span></span><br></pre></td></tr></table></figure></li>
<li>访问对象的属性值可以使用.操作符或者[]操作符。</li>
<li>.语法通常被称为“属性访问”，[‘a’]语法通常被称为“键访问”。这两种语法的主要区别在于.操作符要求属性名满足标识符的命名规则，而[]语法可以接受任意字符串作为属性名，也就是说后者可以传递变量名。</li>
<li>如果你使用sring字面量以外的其他值作为属性名，那它会转换成一个字符串。<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> myObj = &#123;&#125;;</span><br><span class="line">myObj[<span class="literal">true</span>] = <span class="string">&#x27;foo&#x27;</span>;</span><br><span class="line">myObj[<span class="number">3</span>] = <span class="string">&#x27;bar&#x27;</span>;</span><br><span class="line">myObj[myObj] = <span class="string">&#x27;baz&#x27;</span>;</span><br><span class="line"></span><br><span class="line">myObj[<span class="string">&#x27;true&#x27;</span>]; <span class="comment">// &#x27;foo&#x27;</span></span><br><span class="line">myObj[<span class="string">&#x27;3&#x27;</span>]; <span class="comment">// &#x27;bar&#x27;</span></span><br><span class="line">myObj[<span class="string">&#x27;[object object]]&#x27;</span>]; <span class="comment">// &#x27;baz&#x27;</span></span><br></pre></td></tr></table></figure>

</li>
</ul>
<h5 id="3-3-1-可计算属性名"><a href="#3-3-1-可计算属性名" class="headerlink" title="3.3.1 可计算属性名"></a>3.3.1 可计算属性名</h5><ul>
<li>上面提到，如果需要通过表达式来计算属性名，可以使用<code>myObj[prefix + name]</code>的方式。但使用字面量形式来声明时，这样做就不行的。</li>
<li>幸好ES6增加了可计算属性名，可以在字面量形式使用[]包裹一个表达式来当作属性名：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> prefix = <span class="string">&#x27;foo&#x27;</span>;</span><br><span class="line"><span class="keyword">var</span> myObj = &#123;</span><br><span class="line">    [prefix + <span class="string">&#x27;bar&#x27;</span>] : <span class="string">&#x27;hello&#x27;</span>,</span><br><span class="line">    [prefix + <span class="string">&#x27;baz&#x27;</span>] : <span class="string">&#x27;world&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line">myObj[<span class="string">&#x27;foobar&#x27;</span>];    <span class="comment">//hello</span></span><br><span class="line">myObj[<span class="string">&#x27;foobaz&#x27;</span>];    <span class="comment">//world</span></span><br><span class="line"></span><br></pre></td></tr></table></figure></li>
<li>可计算属性名通常用在ES6的符号(Symbol)，它是一种新的基础数据类型。<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> myObj = &#123;</span><br><span class="line">    [<span class="built_in">Symbol</span>.Something] : <span class="string">&#x27;hello world&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//有点类似vuex中store定义函数的用法</span></span><br></pre></td></tr></table></figure>

</li>
</ul>
<h5 id="3-3-2-属性与方法"><a href="#3-3-2-属性与方法" class="headerlink" title="3.3.2 属性与方法"></a>3.3.2 属性与方法</h5><ul>
<li>在传统语言中，普通函数叫做函数，当函数属于某个对象时被称为“方法”。因此，也通常把“方法访问”说成是“方法访问”。</li>
<li>但在JavaScript中，函数永远不会真正“属于”一个对象，并没有把一个函数变成“方法”，函数和对象的关系，最多也只能是间接引用关系。<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>..log(<span class="string">&#x27;foo&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> someFoo = foo;  <span class="comment">//对foo函数的变量引用</span></span><br><span class="line"><span class="keyword">var</span> myObj = &#123;</span><br><span class="line">    someFoo : someFoo</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//输出是一样的</span></span><br><span class="line">foo;</span><br><span class="line">someFoo;</span><br><span class="line">myObj.someFoo;</span><br></pre></td></tr></table></figure></li>
<li>所以，“函数”和“方法”在JavaScript中是一个东西。</li>
</ul>
<h5 id="3-3-3-数组"><a href="#3-3-3-数组" class="headerlink" title="3.3.3 数组"></a>3.3.3 数组</h5><ul>
<li>数组支持以[]的访问形式，期望通过填入整数的数值下标（索引）来获取数组中的元素。<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> arr = [<span class="string">&#x27;foo&#x27;</span>,<span class="number">42</span>,<span class="string">&#x27;bar&#x27;</span>];</span><br><span class="line">arr.length; <span class="comment">//3</span></span><br><span class="line">arr[<span class="number">0</span>]; <span class="comment">//foo</span></span><br><span class="line">arr[<span class="number">2</span>]; <span class="comment">//42</span></span><br></pre></td></tr></table></figure></li>
<li>上文提到，数组是特殊的对象，所以也可以给数组添加属性：（但并不推荐这样做，还是应该用对象来存储键/值对，用数组来存储索引/值对）<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> arr = [<span class="string">&#x27;foo&#x27;</span>,<span class="number">42</span>,<span class="string">&#x27;bar&#x27;</span>];</span><br><span class="line">arr.baz = <span class="string">&#x27;baz&#x27;</span>;</span><br><span class="line">arr.length; <span class="comment">// 3  看到虽然可以给数组添加属性，但length值未发生变化</span></span><br><span class="line">arr.baz; <span class="comment">//baz</span></span><br></pre></td></tr></table></figure></li>
<li>当通过[]进行赋值时，如果属性名看起来像数字，就会被当做索引来使用，所以会直接改变到数组内的元素。</li>
</ul>
<h5 id="3-3-4-赋值对象"><a href="#3-3-4-赋值对象" class="headerlink" title="3.3.4 赋值对象"></a>3.3.4 赋值对象</h5><ul>
<li>JavaScript最常遇到的问题之一，就是如何复制一个对象。<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">anotherFunction</span>(<span class="params"></span>)</span>&#123;&#125;</span><br><span class="line"><span class="keyword">var</span> anotherObj = &#123;</span><br><span class="line">    active : <span class="literal">true</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> anotherArr = [];</span><br><span class="line"><span class="keyword">var</span> myObj = &#123;</span><br><span class="line">    age : <span class="number">2</span>,</span><br><span class="line">    walk : anotherFunction,</span><br><span class="line">    alive : anotherObj,</span><br><span class="line">    scoreHistory : anotherArr</span><br><span class="line">&#125;</span><br><span class="line">anotherArr.push(anotherObj,myObj);</span><br></pre></td></tr></table></figure></li>
<li>我们首先要判断是浅复制还是深复制。如果是浅复制就比较简单了，比如要复制<code>myObj</code>这个对象，里面有值就复制值，有引用就复制引用。</li>
<li>比如ES6中的<code>Object.assign(targetObj,[sourceObj1,sourceObj2])</code>方法可以实现浅复制，参数一是目标对象，参数二可以是一个或多个的源对象。该方法会遍历一个或多个源对象中，所有可枚举的键，并把它们使用=操作符赋值到目标对象，最后返回目标对象。</li>
<li>但对于深复制就比较麻烦了，代码中<code>anotherArr</code>对象和<code>anotherObj</code>互相引用，这时该怎么办呢？为了解决这个棘手的问题，许多javascript框架都提出了自己的解决方案，但具体该采用哪种方法作为标准，没有明确的答案。</li>
</ul>
<h5 id="3-3-5-属性描述符"><a href="#3-3-5-属性描述符" class="headerlink" title="3.3.5 属性描述符"></a>3.3.5 属性描述符</h5><ul>
<li>ES5之前，JavaScript语言没有提供针对属性特性的描述符，比如属性是否只读等。但从ES5开始，所有的属性都具备属性描述符（也称为数据描述符）。<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> myObj = &#123;</span><br><span class="line">    a : <span class="number">2</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></li>
<li>比如<code>myObj.a</code>属性就包含四个描述符：<code>value</code>(值)、<code>writable</code>(可写)、<code>enumerable</code>(可枚举)、<code>configurable</code>(可配置)</li>
<li>ES5提供<code>Object.defineProperty()</code>方法新增或者修改属性并对属性特性进行配置：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> myObj = &#123;&#125;;</span><br><span class="line"><span class="built_in">Object</span>.defineProperty(myObj,<span class="string">&#x27;a&#x27;</span>,&#123;</span><br><span class="line">    value : <span class="number">2</span>,</span><br><span class="line">    writable : <span class="literal">true</span>,</span><br><span class="line">    configurable : <span class="literal">true</span>,</span><br><span class="line">    enumerable : <span class="literal">true</span></span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure></li>
<li><strong>writable</strong> ：决定是否可以修改属性值。当值为false时，非严格模式下默认不能修改值，而在严格模式下，会抛出TypeError错误。</li>
<li><strong>configurable</strong> ：决定属性是否可配置。当值未false时，该属性就不能再通过<code>defineProperty()</code>方法进行修改，同时也无法<code>delete</code>该属性，否则会抛出一个TypeError错误。（注意：把<code>configurable</code>修改为false是单向操作，无法撤销）</li>
<li><strong>enumerable</strong> ：决定属性是否会出现在对象的属性枚举中，比如说<code>for in</code>循环。如果把enumerable设置为false，这个属性就不会出现在枚举中，但仍可以正常访问它。</li>
</ul>
<h5 id="3-3-6-不变性"><a href="#3-3-6-不变性" class="headerlink" title="3.3.6 不变性"></a>3.3.6 不变性</h5><ul>
<li>ES5中有多种方法来让对象或者对象的属性不可改变，但这些方法设置的都是浅不变性，也就是说，只会影响目标对象和它的直接属性，对象引用的其他对象不受影响，仍然是可变的。<ul>
<li><strong>对象变量</strong> ：结合<code>writable:false</code>和<code>configurable:false</code>可以创建一个不可修改、不可重新定义和删除的属性。<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> myObj = &#123;&#125;;</span><br><span class="line"><span class="built_in">Object</span>.defineProperty(myObj,<span class="string">&#x27;NUMBER&#x27;</span>,&#123;</span><br><span class="line">  value : <span class="number">42</span>,</span><br><span class="line">  writable : <span class="literal">false</span>,</span><br><span class="line">  configurable : <span class="literal">false</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></li>
<li><strong>禁止扩展</strong> ：可以用<code>Object.preventExtensions()</code>方法一个对象只保留已有属性，而禁止添加新属性。如果修改，在非严格模式下默认失败，在严格模式下会报TypeError。</li>
<li><strong>密封</strong> ：用<code>Object.seal()</code>方法可以创建一个“密封”对象，密封后的对象，不能新增或删除属性，也不能重新配置属性的特性，但可以修改属性的值。</li>
<li><strong>冻结</strong> : 用<code>Object.freeze()</code>方法可以对一个对象设置级别最高的不可变性，它将禁止对于对象本身及其任意直属属性的修改。</li>
</ul>
</li>
</ul>
<h5 id="3-3-7-Get"><a href="#3-3-7-Get" class="headerlink" title="3.3.7 [[Get]]"></a>3.3.7 [[Get]]</h5><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> myObj = &#123;</span><br><span class="line">    a : <span class="number">2</span></span><br><span class="line">&#125;;</span><br><span class="line">myObj.a;    <span class="comment">//2</span></span><br><span class="line">myObj.b;    <span class="comment">//undefined</span></span><br></pre></td></tr></table></figure>
<ul>
<li><code>myObj.a</code>获取属性实际上是进行了<code>[[Get]]</code>操作（有点像函数调用：<code>[[Get]]()</code>）<ul>
<li>首先在对象中查找是否有名称相同的属性，如果找到就返回这个属性值；</li>
<li>如果没有找到名称相同的属性，则遍历可能存在的<code>[[Prototype]]</code>链（第五章会详细介绍）；</li>
<li>如果无论如何都没有找到名称相同的属性，那<code>[[Get]]</code>操作会返回<code>undefined</code>；</li>
</ul>
</li>
</ul>
<h5 id="3-3-8-Put"><a href="#3-3-8-Put" class="headerlink" title="3.3.8 [[Put]]"></a>3.3.8 [[Put]]</h5><ul>
<li>有可以获取数据的<code>[[Put]]</code>操作，就一定有对应的<code>[[Put]]</code>操作。</li>
<li>当<code>[[Put]]</code>被触发时，首先检查对象中是否已经存在这个属性，如果不存在，会执行更复杂的操作，会在第5章详细讨论。如果存在这个属性，会检查下面的这些内容：<ul>
<li>属性是否是访问描述符(参见3.3.9小结)，如果是描述符，并且存在setter</li>
<li>属性的数据描述符是否<code>writable</code>，如果是false，在非严格模式下静默失败，严格模式下抛出TypeError异常；</li>
<li>如果都不是，将该值设置为属性的值；</li>
</ul>
</li>
</ul>
<h5 id="3-3-9-Getter和Setter"><a href="#3-3-9-Getter和Setter" class="headerlink" title="3.3.9 Getter和Setter"></a>3.3.9 Getter和Setter</h5><ul>
<li>ES5中可以用getter和setter改写默认操作，但只能应用在单个属性上。getter是一个隐藏函数，会在获取属性值时调用。setter也是一个隐藏函数，会在设置属性值时调用。</li>
<li>如果一个对象属性同时定义了getter和setter时，该属性会被定义为“访问描述符”。对于访问描述符来说，JavaScript会忽略属性的value和writable的属性，而只关心set和get特性。<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> myObj = &#123;</span><br><span class="line">    <span class="keyword">get</span> <span class="title">a</span>()&#123;</span><br><span class="line">        <span class="keyword">return</span> a;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="built_in">Object</span>.defineProperty(myObj,<span class="string">&#x27;b&#x27;</span>,&#123;</span><br><span class="line">    get : <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">this</span>.a * <span class="number">2</span>;</span><br><span class="line">    &#125;,</span><br><span class="line">    enumrable : <span class="literal">true</span></span><br><span class="line">&#125;);</span><br><span class="line">myObj.a;    <span class="comment">// 2</span></span><br><span class="line">myObj.b;    <span class="comment">// 4</span></span><br></pre></td></tr></table></figure></li>
<li>通常getter和setter是成对出现的，如果只设置了对象属性的getter，对属性进行set操作会被忽略。<h5 id="3-3-10-存在性"><a href="#3-3-10-存在性" class="headerlink" title="3.3.10 存在性"></a>3.3.10 存在性</h5></li>
<li>有个问题，我们进行属性访问时，返回值可能是undefined，而这个值有可能是属性存储中的undefined，也有可能是属性不存在而返回的undefined。那么应该如何区分呢？<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> myObj = &#123;</span><br><span class="line">    a : <span class="number">2</span></span><br><span class="line">&#125;;</span><br><span class="line">(<span class="string">&quot;a&quot;</span> <span class="keyword">in</span> myObj); <span class="comment">//true</span></span><br><span class="line">(<span class="string">&quot;b&quot;</span> <span class="keyword">in</span> myObj); <span class="comment">//false</span></span><br><span class="line">myObj.hasOwnProperty(<span class="string">&quot;a&quot;</span>);  <span class="comment">// true</span></span><br><span class="line">myObj.hasOwnProperty(<span class="string">&quot;b&quot;</span>);  <span class="comment">// false</span></span><br></pre></td></tr></table></figure></li>
<li><code>in</code>操作符检查属性是否在对象以及其[[Prototype]]原型链中，而<code>hasOwnProperty()</code>则只会检查属性是否在对象中，不会检查[[Prototype]]原型链。</li>
<li>除了通过for循环来确定属性是否可枚举之外，<code>propertyIsEnumerable()</code>方法也可以检查属性名是否在对象中，并符合<code>enumerable:true</code>。</li>
<li>另外，<code>Object.keys()</code>和<code>Object.getOwnPropertyNames()</code>方法会返回对象中可枚举的属性数组。</li>
</ul>
<h3 id="3-4-遍历"><a href="#3-4-遍历" class="headerlink" title="3.4 遍历"></a>3.4 遍历</h3><ul>
<li>关于遍历属性值，除了可以用标准的for循环，ES5还增加了一些数组的辅助迭代器：<code>forEach()</code>、<code>every()</code>、<code>some()</code><ul>
<li><code>forEach()</code>会遍历数组中的所有值，并忽略回调函数的返回值；</li>
<li><code>every()</code>会一直运行直到回调函数返回false；</li>
<li><code>some()</code>会一直运行直到回调函数返回true；</li>
</ul>
</li>
<li>ES6增加了一种用来遍历数组的<code>for of</code>循环语法：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> myArray = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>];</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">var</span> item <span class="keyword">of</span> myArray)&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(item);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 1</span></span><br><span class="line"><span class="comment">// 2</span></span><br><span class="line"><span class="comment">// 3</span></span><br></pre></td></tr></table></figure></li>
<li><code>for of</code>循环的工作原理是，会向被访问对象请求一个迭代器对象（<code>iterator</code>），然后通过调用迭代器对象的<code>next()</code>方法来遍历所有返回值。<code>next()</code>方法会返回形式为<code>&#123;value:...,done:...&#125;</code>的值，value是当前的遍历值，done是一个布尔值，表示十分还有可以遍历的值。</li>
</ul>
<h3 id="3-5-小结"><a href="#3-5-小结" class="headerlink" title="3.5 小结"></a>3.5 小结</h3><ul>
<li>JavaScript中的声明对象有两种方式：字面量形式和构造形式。字面量形式更常用，但构造形式可以提供更多选项。</li>
<li>对象是6个基础类型之一，对象还包含具有不同行为的特殊子类型，比如function。</li>
<li>对象就是键值对的集合。可以通过<code>.propName</code>或者<code>[propName]</code>语法来获取属性值。访问属性时，引擎实际上会调用内部默认的<code>[[Get]]</code>操作，<code>[[Get]]</code>操作会检查对象本身是否包含这个属性，如果没有找到还会查找[[Protype]]原型链。</li>
<li>属性的特性可以通过属性描述符来控制，比如<code>writable</code>和<code>configurable</code>。此外，<code>Object.preventExtensions()</code>、<code>Object.seal()</code>和<code>Obejct.freeze()</code>可以设置对象不可变性的级别。</li>
<li>属性不一定包含值，也有可能是具备了getter/setter的访问描述符。</li>
<li>可以使用ES6的<code>for...of</code>语法来遍历数组或对象中的值，<code>for...of</code>循环会寻找内置或自定义的<code>@@iterator</code>对象并调用它的<code>next()</code>方法来遍历数据值。</li>
</ul>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://gitee.com/lianghuilin/murmur.git/reading_notes/%E3%80%8A%E4%BD%A0%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84JavaScript-%E4%B8%8A-%E3%80%8BS02E02/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/murmur/images/avatar.gif">
      <meta itemprop="name" content="William Leung">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="汇话板">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/murmur/reading_notes/%E3%80%8A%E4%BD%A0%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84JavaScript-%E4%B8%8A-%E3%80%8BS02E02/" class="post-title-link" itemprop="url">《你不知道的JavaScript(上)》S02E02</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>
      

      <time title="创建时间：2020-12-01 22:20:17 / 修改时间：23:07:11" itemprop="dateCreated datePublished" datetime="2020-12-01T22:20:17+08:00">2020-12-01</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/murmur/categories/%E4%B8%80%E5%91%A8%E4%B8%80%E7%AB%A0%E5%89%8D%E7%AB%AF%E4%B9%A6/" itemprop="url" rel="index"><span itemprop="name">一周一章前端书</span></a>
        </span>
    </span>

  
    <span class="post-meta-item" title="本文字数">
      <span class="post-meta-item-icon">
        <i class="far fa-file-word"></i>
      </span>
      <span class="post-meta-item-text">本文字数：</span>
      <span>8.3k</span>
    </span>
    <span class="post-meta-item" title="阅读时长">
      <span class="post-meta-item-icon">
        <i class="far fa-clock"></i>
      </span>
      <span class="post-meta-item-text">阅读时长 &asymp;</span>
      <span>8 分钟</span>
    </span>
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="第2章：this全面解析"><a href="#第2章：this全面解析" class="headerlink" title="第2章：this全面解析"></a>第2章：this全面解析</h1><h2 id="2-1-调用位置"><a href="#2-1-调用位置" class="headerlink" title="2.1 调用位置"></a>2.1 调用位置</h2><ul>
<li>在理解this的绑定之前，首先要理解“函数调用的位置”，即函数在代码中被调用的位置。只有仔细分析了调用位置才能解释，函数中的this到底引用的是什么？</li>
<li>寻找“函数被调用的位置”，其实并没有想象中的简单，因为JS是很灵活的语言，经常将函数也作为参数进行传递，可能会隐藏真正的调用位置。</li>
<li>所以我们需要分析函数的调用栈。所谓调用栈，就是为了到达当前执行位置，所调用过的所有函数，所以我们可以把调用栈想象成一个函数调用链，举例说明：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">baz</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="comment">//当前位置是baz</span></span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">&quot;my name is baz&quot;</span>);</span><br><span class="line">    <span class="comment">//baz中调用bar</span></span><br><span class="line">    bar();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">bar</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="comment">//当前位置是bar</span></span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">&quot;my name is bar&quot;</span>);</span><br><span class="line">    <span class="comment">//bar中调用foo</span></span><br><span class="line">    foo();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="comment">//当前位置是foo</span></span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">&quot;my name is foo&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//window下调用baz</span></span><br><span class="line">baz();</span><br><span class="line"></span><br><span class="line"><span class="comment">/* </span></span><br><span class="line"><span class="comment"> * 所以foo的调用栈(链)就是：</span></span><br><span class="line"><span class="comment"> * window -&gt; baz -&gt; bar -&gt; foo</span></span><br><span class="line"><span class="comment"> * /</span></span><br></pre></td></tr></table></figure>

</li>
</ul>
<h2 id="2-2-绑定规则"><a href="#2-2-绑定规则" class="headerlink" title="2.2 绑定规则"></a>2.2 绑定规则</h2><ul>
<li>通过函数的调用位置，并应用JavaScript中四条决定this绑定的规则，就能分析this的引用值了。<h4 id="2-2-1-默认绑定"><a href="#2-2-1-默认绑定" class="headerlink" title="2.2.1 默认绑定"></a>2.2.1 默认绑定</h4></li>
<li>首先是最常用的函数调用类型：独立函数调用。所谓独立函数调用，就是没有应用其他规则的默认调用规则。举例：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="number">2</span>;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="built_in">this</span>.a);   </span><br><span class="line">&#125;</span><br><span class="line">foo();  <span class="comment">//输出 2</span></span><br></pre></td></tr></table></figure></li>
<li>当调用<code>foo()</code>函数时，<code>this.a</code>指向了全局变量<code>a</code>，因为在默认绑定下，<code>this</code>指向全局对象。</li>
<li>那如何辨别这里应用的是默认绑定呢？这时候，就需要运用我们前面讲的“分析函数调用位置”了，在这段代码中，<code>foo()</code>函数是直接调用的，不带任何修饰，也不被任何函数包含，所以可以确定是默认绑定。<blockquote>
<p>注意：如果 <strong>函数内使用严格模式(strict mode)</strong> ，是不能将全局对象用于默认绑定的，最终<code>this</code>会绑定到<code>undefined</code>上。举例说明：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="number">2</span>;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line"><span class="meta">  &quot;use strict&quot;</span>;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="built_in">this</span>.a);</span><br><span class="line">&#125;</span><br><span class="line">foo();    <span class="comment">//输出 TypeError : this is undefined</span></span><br></pre></td></tr></table></figure>
<p>但 <strong>在严格模式下调用函数</strong> ，则不影响默认绑定。举例说明：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">var a &#x3D; 2;</span><br><span class="line">function foo()&#123;</span><br><span class="line">  console.log(this.a);</span><br><span class="line">&#125;</span><br><span class="line">(function()&#123;</span><br><span class="line">  &quot;use strict&quot;;</span><br><span class="line">  foo():  &#x2F;&#x2F; 输出2</span><br><span class="line">)();</span><br></pre></td></tr></table></figure>
<p>由于我们可能会使用众多第三方库，所以代码中可能会混合使用strict模式和非strict模式，因此一定要注意这类的兼容性问题。</p>
</blockquote>
</li>
</ul>
<h4 id="2-2-2-隐式绑定"><a href="#2-2-2-隐式绑定" class="headerlink" title="2.2.2 隐式绑定"></a>2.2.2 隐式绑定</h4><ul>
<li>第二条规则，就是通过函数调用位置，函数是否属于某个对象的属性。<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> obj = &#123;</span><br><span class="line">    a : <span class="number">2</span>,</span><br><span class="line">    foo : foo</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="built_in">this</span>.a);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">obj.foo();  <span class="comment">// 输出 2</span></span><br></pre></td></tr></table></figure></li>
<li>你看<code>foo()</code>方法的声明方式，它是被当做引用属性添加到了<code>obj</code>对象中，这种情况下，<code>obj</code>对象拥有/包含了<code>foo()</code>方法。</li>
<li>当函数有包含自己的对象时，隐式绑定规则会把<code>this</code>绑定到这个对象。</li>
<li>因此，调用<code>foo()</code>时，<code>this</code>被绑定到了<code>obj</code>对象，在函数中<code>this.a</code>和<code>obj.a</code>的引用是一样的。</li>
<li>值得注意的是，如果是多层嵌套对象下的函数，就只在最后一层中起作用。举例：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="built_in">this</span>.a);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> obj1 = &#123;</span><br><span class="line">    a : <span class="number">2</span>,</span><br><span class="line">    obj2 : &#123;</span><br><span class="line">        a : <span class="number">42</span>,</span><br><span class="line">        foo : foo</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">obj1.obj2.foo();    <span class="comment">//输出 42</span></span><br></pre></td></tr></table></figure></li>
<li>值得注意的是，如果将<code>obj1.obj2.foo</code>函数的引用赋值给另一个变量，然后以默认绑定的方式调用函数，不管是自定义的函数，还是JS的内置函数，则还是会应用默认绑定规则：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="string">&#x27;oops,global&#x27;</span>;</span><br><span class="line"><span class="keyword">var</span> bar = obj1.obj2.foo;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">runFoo</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    obj1.obj2.foo();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 都是输出 &#x27;oops,global&#x27;</span></span><br><span class="line">bar();  </span><br><span class="line">runfoo();   </span><br><span class="line"><span class="built_in">setTimeout</span>(obj1.obj2.foo,<span class="number">100</span>);</span><br></pre></td></tr></table></figure>

</li>
</ul>
<h4 id="2-2-3-显示绑定"><a href="#2-2-3-显示绑定" class="headerlink" title="2.2.3 显示绑定"></a>2.2.3 显示绑定</h4><ul>
<li>如果不想在对象内部包含函数引用，想在某个对象上强制调用函数，该怎么做呢?</li>
<li>JavaScript中的函数都有一些特性，可以用来解决这个问题。比如函数的<code>call()</code>和<code>applay()</code>方法</li>
<li>这两个方法，传入的第一个参数是一个对象，就是留给<code>this</code>准备的，调用时会将其绑定到<code>this</code>。因为可以直接指定<code>this</code>的绑定对象，因此我们称之为显示绑定。<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="built_in">this</span>.a);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> obj = &#123;</span><br><span class="line">    a : <span class="number">2</span></span><br><span class="line">&#125;;</span><br><span class="line">foo.call(obj);  <span class="comment">// 输出 2</span></span><br></pre></td></tr></table></figure></li>
<li>但如果传入的参数是原始值（字符串、布尔或者数值类型）当做<code>this</code>的绑定对象的话，这个原始值会被转换成它的对象形式。也就是<code>new String()</code>、<code>new Boolean()</code>或者<code>new Number()</code>，这个过程通常叫做 <strong>装箱</strong> 。</li>
<li><strong>1. 硬绑定</strong><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="built_in">this</span>.a);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> obj = &#123;</span><br><span class="line">    a : <span class="number">2</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> bar = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    foo.call(obj);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">bar();  <span class="comment">// 2</span></span><br><span class="line"><span class="built_in">setTimeout</span>(bar,<span class="number">100</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">//硬绑定不能再修改它的this</span></span><br><span class="line">bar.call(<span class="built_in">window</span>);   <span class="comment">// 2</span></span><br></pre></td></tr></table></figure></li>
<li>函数<code>bar()</code>在它内部手动调用了<code>foo.call(obj)</code>，强制把<code>foo</code>的<code>this</code>绑定到了<code>obj</code>。无论之后如何调用函数<code>bar</code>，总会手动在<code>obj</code>上调用<code>foo</code>。这种显式的强制绑定，称之为硬绑定。</li>
<li>硬绑定的典型应用场景就是创建一个包裹函数，负责接收参数并返回值：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params">something</span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="built_in">this</span>.a,something);</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">this</span>.a + something;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> obj = &#123;</span><br><span class="line">    a : <span class="number">2</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> bar = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> foo.apply(obj,<span class="built_in">arguments</span>);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> b = bar(<span class="number">3</span>); <span class="comment">// 2 3</span></span><br><span class="line"><span class="built_in">console</span>.log(b); <span class="comment">// 5</span></span><br></pre></td></tr></table></figure></li>
<li>另一种方式就是创建一个可以重复使用的辅助函数：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params">something</span>)</span>&#123;</span><br><span class="line">    cosnole.log(<span class="built_in">this</span>.a,something);</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">this</span>.a + something;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">bind</span>(<span class="params">fn,obj</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> fn.apply(obj,<span class="built_in">arguments</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> obj = &#123;</span><br><span class="line">    a : <span class="number">2</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> bar = bind(foo,obj);</span><br><span class="line"><span class="keyword">var</span> b = bar(<span class="number">3</span>);    <span class="comment">// 2 3</span></span><br><span class="line"><span class="built_in">console</span>.log(b);    <span class="comment">// 5</span></span><br></pre></td></tr></table></figure></li>
<li>由于硬绑定是一种非常常用的模式，所以ES5提供了内置方法<code>Function.prototype.bind</code>：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params">something</span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="built_in">this</span>.a,something);</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">this</span>.a + something;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> obj = &#123;</span><br><span class="line">    a : <span class="number">2</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> bar = foo.bind(obj);</span><br><span class="line"><span class="keyword">var</span> b = bar(<span class="number">3</span>); <span class="comment">// 2 3</span></span><br><span class="line"><span class="built_in">console</span>.log(b);    <span class="comment">// 5</span></span><br></pre></td></tr></table></figure></li>
<li><code>bind()</code>会返回一个硬编码的新函数，它会把你指定的参数设置为<code>this</code> 的上下文，并调用原始函数。</li>
<li><strong>2. API调用的“上下文”</strong></li>
<li>许多函数都提供了一个可选的参数，其作用和<code>bind()</code>函数一样，确保你的回调函数使用指定的<code>this</code>。举例：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params">el</span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(el,<span class="built_in">this</span>.id);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> obj = &#123;</span><br><span class="line">    id : <span class="string">&#x27;awesome&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line">[<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>].forEach(foo,obj);</span><br></pre></td></tr></table></figure></li>
<li>通过<code>call()</code>和<code>apply()</code>实现显示绑定，可以少写代码。<h4 id="2-2-4-new绑定"><a href="#2-2-4-new绑定" class="headerlink" title="2.2.4 new绑定"></a>2.2.4 new绑定</h4></li>
<li>在讲解最后一条<code>this</code>的绑定规则之前，首先要澄清一个常见的关于JavaScript中函数和对象的误解。</li>
<li>在传统的面向类的语言中，“构造函数”是类中的一些特殊方法，使用new初始化类时会调用类的构造函数，<code>something = new MyClass()</code>。然而，JavaScript中的new的机制实际上和面向类的语言完全不同。</li>
<li>我们重新定义一些JavaScript中的“构造函数”：在JavaScript中，构造函数只是使用new操作符时被调用的函数，它们并不属于某一个对象，也不会实例化一个类。</li>
<li>所有函数都可以用new来调用，这种函数调用被称为构造函数调用。实际上，并不存在所谓的“构造函数”，只有对函数的“构造调用”。</li>
<li>使用new来调用函数，会自动执行下面的操作：<ol>
<li>创建一个全新的对象</li>
<li>这个新对象会被执行[[Prototype]]连接</li>
<li>这个新对象会绑定到函数调用的this</li>
<li>如果函数没有返回其他对象，那么new表达式的函数调用会自动返回这个新对象。<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params">a</span>)</span>&#123;</span><br><span class="line">    <span class="built_in">this</span>.a = a;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> bar = <span class="keyword">new</span> foo(<span class="number">2</span>);</span><br><span class="line"><span class="built_in">console</span>.log(bar.a); <span class="comment">// 2</span></span><br></pre></td></tr></table></figure></li>
</ol>
</li>
<li>使用new来调用<code>foo()</code>时，我们会构造一个新对象，把它绑定到<code>foo()</code>调用中的this上，我们称之为new绑定。<h2 id="2-3-优先级"><a href="#2-3-优先级" class="headerlink" title="2.3 优先级"></a>2.3 优先级</h2></li>
<li>上文通过大篇幅讲了函数调用中，this绑定的四条规则：默认绑定、隐式绑定、显示绑定和new绑定。但如果调用应用了多条规则就必须给这些规则设定优先级了。</li>
<li>毫无疑问，默认绑定的优先级是最低的，暂不考虑它。</li>
<li>隐式绑定和显示绑定哪一个优先级更高？我们来测试一下：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="built_in">this</span>.a);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> obj1 = &#123;</span><br><span class="line">    a : <span class="number">2</span>,</span><br><span class="line">    foo : foo</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> obj2 = &#123;</span><br><span class="line">    a : <span class="number">3</span>,</span><br><span class="line">    foo : foo</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">obj1.foo(); <span class="comment">//2</span></span><br><span class="line">obj2.foo(); <span class="comment">//3</span></span><br><span class="line"></span><br><span class="line">obj1.foo.call(obj2);    <span class="comment">// 3</span></span><br><span class="line">obj2.foo.call(obj1);    <span class="comment">// 3</span></span><br></pre></td></tr></table></figure></li>
<li>可以看到，显式绑定优先级更高。</li>
<li>接下来，我们要测试，new绑定和隐式绑定的优先级谁高谁低：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params">something</span>)</span>&#123;</span><br><span class="line">    <span class="built_in">this</span>.a = something;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> obj1 = &#123;</span><br><span class="line">    foo : foo</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> obj2 = &#123;&#125;</span><br><span class="line"></span><br><span class="line">obj1.foo(<span class="number">2</span>);</span><br><span class="line"><span class="built_in">console</span>.log(obj1.a);    <span class="comment">//2</span></span><br><span class="line"></span><br><span class="line">obj1.foo.call(obj2,<span class="number">3</span>);</span><br><span class="line"><span class="built_in">console</span>.log(obj2.a);    <span class="comment">//3</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> bar = <span class="keyword">new</span> obj1.foo(<span class="number">4</span>);</span><br><span class="line"><span class="built_in">console</span>.log(obj1.a);    <span class="comment">//2</span></span><br><span class="line"><span class="built_in">console</span>.log(bar.a);    <span class="comment">//2</span></span><br></pre></td></tr></table></figure></li>
<li>可以看到new绑定比隐式绑定的优先级更高，<code>obj1.a</code>的值一直没改变。</li>
<li>那new绑定和显示绑定，谁的优先级更高呢？（由于new和call/apply无法一起使用，所以通过硬绑定来测试）<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params">something</span>)</span>&#123;</span><br><span class="line">    <span class="built_in">this</span>.a = something;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> obj1 = &#123;&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> bar = foo.bind(obj1);</span><br><span class="line">bar(<span class="number">2</span>);</span><br><span class="line"><span class="built_in">console</span>.log(obj1.a);    <span class="comment">//2</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> baz = <span class="keyword">new</span> bar(<span class="number">3</span>);</span><br><span class="line"><span class="built_in">console</span>.log(obj1.a);    <span class="comment">// 2</span></span><br><span class="line"><span class="built_in">console</span>.log(baz.a);     <span class="comment">// 3</span></span><br></pre></td></tr></table></figure></li>
<li>观察输出的结果，<code>bar</code>被硬绑定到了<code>obj1</code>上，但<code>new baz(3)</code>并没有把<code>obj1.a</code>修改为3.</li>
<li>话说回来，之所以在new中使用硬绑定函数，主要目的是想预先设置一些参数，这样在使用new进行初始化时就可以传入其他参数了。举例：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params">p1,p2</span>)</span>&#123;</span><br><span class="line">    <span class="built_in">this</span>.val = p1 + p2;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> bar = foo.bind(<span class="literal">null</span>,<span class="string">&quot;p1&quot;</span>);</span><br><span class="line"><span class="keyword">var</span> baz = <span class="keyword">new</span> bar(<span class="string">&quot;p2&quot;</span>);</span><br><span class="line">baz.val;    <span class="comment">//p1p2</span></span><br></pre></td></tr></table></figure></li>
<li>根据优先级就能判断函数调用时应用的是哪条规则了，判断的步骤：<ol>
<li> 函数是否进行了new调用，如果是的话，this绑定的是新创建的对象；</li>
<li> 函数是否通过<code>call</code>、<code>apply</code>或者硬绑定调用，如果有的话，this绑定的是指定的对象；</li>
<li> 函数是否在某个对象中调用，如果是的话，this绑定的是该对象；</li>
<li> 如果都不是的话，使用默认绑定，绑定到全局对象。（严格模式下，绑定到undefined）</li>
</ol>
</li>
</ul>
<h2 id="2-4-绑定例外"><a href="#2-4-绑定例外" class="headerlink" title="2.4 绑定例外"></a>2.4 绑定例外</h2><h4 id="2-4-1-被忽略的this"><a href="#2-4-1-被忽略的this" class="headerlink" title="2.4.1 被忽略的this"></a>2.4.1 被忽略的this</h4><ul>
<li>把null或者undefined作为this传入call、apply或者bind，这些值在函数调用时会被忽略，应用默认的绑定规则：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="built_in">this</span>.a);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> a = <span class="number">2</span>;</span><br><span class="line">foo.call(<span class="literal">null</span>); <span class="comment">// 2</span></span><br></pre></td></tr></table></figure></li>
<li>什么场景下会传入null呢？比如使用apply来遍历输出一个数组，或者通过<code>bind()</code>进行柯里化：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params">a,b</span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">&#x27;a:&#x27;</span>+a+<span class="string">&#x27;b:&#x27;</span>+b);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">foo.apply(<span class="literal">null</span>,[<span class="number">2</span>,<span class="number">3</span>]); <span class="comment">// a:2,b:3</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//先预先传入参数a</span></span><br><span class="line"><span class="keyword">var</span> bar = foo.bind(<span class="literal">null</span>,<span class="number">2</span>);</span><br><span class="line"><span class="comment">//调用时再传入参数b</span></span><br><span class="line">bar(<span class="number">3</span>); <span class="comment">// a:2,b:3</span></span><br></pre></td></tr></table></figure></li>
<li>但这种方式可能会导致许多难以分析和追踪的bug，我们可以用更安全的方式。</li>
<li>更安全的做法就是不传入null，而是传入一个空的对象，把this绑定到这个对象，就好像创建一个非军事区的隔离对象一样，以确保不会对你的程序产生任何副作用。<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params">a,b</span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">&#x27;a:&#x27;</span>+a+<span class="string">&#x27;b:&#x27;</span>+b);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> o = <span class="built_in">Object</span>.create(<span class="literal">null</span>);</span><br><span class="line">foo.apply(o,[<span class="number">2</span>,<span class="number">3</span>]); <span class="comment">// a:2,b:3</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//先预先传入参数a</span></span><br><span class="line"><span class="keyword">var</span> bar = foo.bind(o,<span class="number">2</span>);</span><br><span class="line"><span class="comment">//调用时再传入参数b</span></span><br><span class="line">bar(<span class="number">3</span>); <span class="comment">// a:2,b:3</span></span><br></pre></td></tr></table></figure></li>
<li>我们通过<code>Object.create(null)</code>来创建对象，它和直接以字面量<code>&#123;&#125;</code>创建对象很相似，但前者不会创建<code>Object.prototype</code>的委托，所以它比<code>&#123;&#125;</code>更空。</li>
</ul>
<h4 id="2-4-2-间接引用"><a href="#2-4-2-间接引用" class="headerlink" title="2.4.2 间接引用"></a>2.4.2 间接引用</h4><ul>
<li>另一个需要注意的是，可能会有意无意的创建一个函数的“间接引用”，而在这种情况下，调用这个函数会应用默认绑定规则。间接引用最容易在赋值时发生：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="built_in">this</span>.a);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> a = <span class="number">2</span>;</span><br><span class="line"><span class="keyword">var</span> o = &#123;</span><br><span class="line">    a : <span class="number">3</span>,</span><br><span class="line">    foo : foo</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> p = &#123; </span><br><span class="line">    a : <span class="number">4</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">o.foo(); <span class="comment">// 3</span></span><br><span class="line">(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    p.foo = o.foo</span><br><span class="line">&#125;)();   <span class="comment">// 2</span></span><br></pre></td></tr></table></figure>
<h4 id="2-4-3-软绑定"><a href="#2-4-3-软绑定" class="headerlink" title="2.4.3 软绑定"></a>2.4.3 软绑定</h4></li>
<li>硬绑定可以把this强制绑定到指定的对象，以防止函数调用应用默认绑定规则。问题在于，硬绑定会大大降低函数的灵活性，硬绑定之后，就无法使用隐式绑定或者显示绑定来修改this。<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(!<span class="built_in">Function</span>.prototype.softbind)&#123;</span><br><span class="line">    <span class="built_in">Function</span>.prototype.softbind = <span class="function"><span class="keyword">function</span>(<span class="params">obj</span>)</span>&#123;</span><br><span class="line">        <span class="keyword">var</span> fn = <span class="built_in">this</span>;</span><br><span class="line">        <span class="keyword">var</span> curried = [].slice.call(<span class="built_in">arguments</span>,<span class="number">1</span>);</span><br><span class="line">        <span class="keyword">var</span> bound = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">            <span class="keyword">return</span> fn.apply(</span><br><span class="line">                (!<span class="built_in">this</span> || <span class="built_in">this</span> === (<span class="built_in">window</span> || <span class="built_in">global</span>)) ? obj : <span class="built_in">this</span>,</span><br><span class="line">                curried.concat.apply(curried,<span class="built_in">arguments</span>)</span><br><span class="line">            );</span><br><span class="line">        &#125;;</span><br><span class="line">        bound.prototype = <span class="built_in">Object</span>.create(fn.prototype);</span><br><span class="line">        <span class="keyword">return</span> bound;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">&#x27;name:&#x27;</span> + <span class="built_in">this</span>.name);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> obj = &#123;</span><br><span class="line">        name : <span class="string">&quot;obj&quot;</span></span><br><span class="line">    &#125;,</span><br><span class="line">    obj2 = &#123;</span><br><span class="line">        name : <span class="string">&quot;obj2&quot;</span></span><br><span class="line">    &#125;,</span><br><span class="line">    obj3 = &#123;</span><br><span class="line">        name : <span class="string">&quot;obj3&quot;</span></span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> fooOBJ = foo.softbind(obj);</span><br><span class="line"></span><br><span class="line">fooOBJ();   <span class="comment">// name : obj</span></span><br><span class="line"></span><br><span class="line">obj2.foo = foo.softbind(obj);</span><br><span class="line">obj2.foo(); <span class="comment">// name : obj2</span></span><br><span class="line"></span><br><span class="line">fooOBJ.call(obj3);  <span class="comment">// name : obj3</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">setTimeout</span>(obj2.foo,<span class="number">10</span>);    <span class="comment">// name : obj</span></span><br><span class="line"></span><br></pre></td></tr></table></figure>
<h2 id="2-5-this词法"><a href="#2-5-this词法" class="headerlink" title="2.5 this词法"></a>2.5 this词法</h2></li>
<li>前面解说的四条规则几乎也包含所有函数，但ES6中介绍了一种无法使用这些规则的特殊函数类型：__箭头函数__。</li>
<li>箭头函数使用操作符<code>=&gt;</code>来定义，箭头函数不适用this的四种标准规则，而是根据外层作用域来决定this。<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="function">(<span class="params">a</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="built_in">this</span>.a);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> obj1 = &#123;</span><br><span class="line">    a : <span class="number">2</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> obj2 = &#123;</span><br><span class="line">    a : <span class="number">3</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> bar = foo.call(obj1);</span><br><span class="line">bar.call(obj2); <span class="comment">// 2 , 不是 3 !</span></span><br></pre></td></tr></table></figure></li>
<li>箭头函数最常用于回调函数中，例如事件处理器或者定时器：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="built_in">this</span>.a);</span><br><span class="line">    &#125;,<span class="number">100</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> obj = &#123;</span><br><span class="line">    a : <span class="number">2</span></span><br><span class="line">&#125;;</span><br><span class="line">foo.call(obj); <span class="comment">// 2</span></span><br></pre></td></tr></table></figure>

</li>
</ul>
<h2 id="2-6-小结"><a href="#2-6-小结" class="headerlink" title="2.6 小结"></a>2.6 小结</h2><ul>
<li>如果要判断一个运用中函数的this绑定，需要找到函数的调用位置，然后按顺序应用四条规则来判断this的绑定对象：<ol>
<li>由new调用？绑定到新创建的对象；</li>
<li>由call或者apply/bind调用？绑定到指定的对象；</li>
<li>由对象调用？绑定到那个对象；</li>
<li>默认：严格模式下绑定到undefined，否则绑定到全局对象；</li>
</ol>
</li>
<li>有些调用无意中使用默认绑定规则。如果想“更安全” 地忽略this绑定，可以使用一个空的临时对象，比如<code>o = Object.create(null)</code>，以保护全局对象。</li>
<li>ES6中的箭头函数并不会使用四条标准的绑定规则，而是根据当前的词法作用域来决定this。</li>
</ul>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://gitee.com/lianghuilin/murmur.git/reading_notes/%E3%80%8A%E4%BD%A0%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84JavaScript-%E4%B8%8A-%E3%80%8BS02E01/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/murmur/images/avatar.gif">
      <meta itemprop="name" content="William Leung">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="汇话板">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/murmur/reading_notes/%E3%80%8A%E4%BD%A0%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84JavaScript-%E4%B8%8A-%E3%80%8BS02E01/" class="post-title-link" itemprop="url">《你不知道的JavaScript(上)》S02E01</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>
      

      <time title="创建时间：2020-12-01 22:18:24 / 修改时间：23:07:10" itemprop="dateCreated datePublished" datetime="2020-12-01T22:18:24+08:00">2020-12-01</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/murmur/categories/%E4%B8%80%E5%91%A8%E4%B8%80%E7%AB%A0%E5%89%8D%E7%AB%AF%E4%B9%A6/" itemprop="url" rel="index"><span itemprop="name">一周一章前端书</span></a>
        </span>
    </span>

  
    <span class="post-meta-item" title="本文字数">
      <span class="post-meta-item-icon">
        <i class="far fa-file-word"></i>
      </span>
      <span class="post-meta-item-text">本文字数：</span>
      <span>2k</span>
    </span>
    <span class="post-meta-item" title="阅读时长">
      <span class="post-meta-item-icon">
        <i class="far fa-clock"></i>
      </span>
      <span class="post-meta-item-text">阅读时长 &asymp;</span>
      <span>2 分钟</span>
    </span>
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="第1章：关于this"><a href="#第1章：关于this" class="headerlink" title="第1章：关于this"></a>第1章：关于this</h1><h3 id="1-1-为什么要用this"><a href="#1-1-为什么要用this" class="headerlink" title="1.1 为什么要用this"></a>1.1 为什么要用this</h3><ul>
<li><code>this</code>是一个很特别的关键字，同时也是JavaScript中很复杂的机制，本章讲解<code>this</code>。</li>
<li>我们先看看，不用<code>this</code>的代码是怎么样的：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> Me = &#123;</span><br><span class="line">    name : <span class="string">&#x27;William&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> You = &#123;</span><br><span class="line">    name : <span class="string">&#x27;Yoki&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//公共方法:返回指定对象的大写name</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">identify</span>(<span class="params">context</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> context.name.toUpperCase(); </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//公共方法:用指定对象大写的name，输出hello字符串</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">speak</span>(<span class="params">context</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">var</span> greeting = <span class="string">&quot;Hello,I&#x27;m &quot;</span> + identify(context);</span><br><span class="line">    <span class="built_in">console</span>.log(greeting);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(identify(You));</span><br><span class="line"><span class="built_in">console</span>.log(identify(Me));</span><br><span class="line">speak(Me);</span><br><span class="line">speak(You);</span><br><span class="line">	        </span><br></pre></td></tr></table></figure></li>
<li><code>speak()</code>和<code>identify()</code>方法不使用<code>this</code>，则需要显式的传入<code>context</code>对象，而随着代码越来越复杂，显式传递对象的方式会使得代码愈加混乱。</li>
<li>接着我们改写这两个方法，会发现，其实<code>this</code>提供了一种更优雅的方式隐式传递对象的引用，这样API也可以设计得更加简洁且复用。<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">identify</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">this</span>.name.toUpperCase(); </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">speak</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">var</span> greeting = <span class="string">&quot;Hello,I&#x27;m &quot;</span> + identify.call(<span class="built_in">this</span>);</span><br><span class="line">    <span class="built_in">console</span>.log(greeting);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(identify.call(You));</span><br><span class="line"><span class="built_in">console</span>.log(identify.call(Me));</span><br><span class="line">speak(Me);</span><br><span class="line">speak(You);</span><br></pre></td></tr></table></figure>

</li>
</ul>
<h3 id="1-2-this的误解"><a href="#1-2-this的误解" class="headerlink" title="1.2 this的误解"></a>1.2 this的误解</h3><ul>
<li>人们很容易把<code>this</code>理解成指向函数自身，这从英语的语法上理解是没毛病的，但事实并不是这样的。<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params">num</span>)</span>&#123;</span><br><span class="line">   <span class="built_in">console</span>.log(<span class="string">&quot;foo:&quot;</span> + num);</span><br><span class="line">   <span class="built_in">this</span>.count++;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">foo.count = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">var</span> i=<span class="number">0</span>;i&lt;<span class="number">10</span>;i++)&#123;</span><br><span class="line">    <span class="keyword">if</span>(i &gt; <span class="number">5</span>)&#123;</span><br><span class="line">        foo(i);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(foo.count);</span><br><span class="line"><span class="built_in">console</span>.log(count);</span><br></pre></td></tr></table></figure>
控制台输出的是：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">foo:<span class="number">6</span></span><br><span class="line">foo:<span class="number">7</span></span><br><span class="line">foo:<span class="number">8</span></span><br><span class="line">foo:<span class="number">9</span></span><br><span class="line"><span class="number">0</span></span><br><span class="line"><span class="literal">NaN</span></span><br></pre></td></tr></table></figure></li>
<li>从上述打印情况来看，<code>console.log()</code>语句确实产生了4条输出，证明<code>foo()</code>方法被调用了4次，但<code>this.count</code>的指向并不是<code>foo.count</code>，反而在<code>window</code>下多出了一个<code>count</code>，值为<code>NaN</code>。</li>
<li>为了修复上述的结果，当然可以将<code>foo</code>方法中的<code>this.count++</code>改成<code>foo.count++</code>，但忽略了真正的问题——<code>this</code>究竟指向谁？</li>
<li>下面我们来真正修复这个问题，其实只需要修改<code>foo()</code>方法调用的方式即可：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span>(<span class="keyword">var</span> i=<span class="number">0</span>;i&lt;<span class="number">10</span>;i++)&#123;</span><br><span class="line">    <span class="keyword">if</span>(i &gt; <span class="number">5</span>)&#123;</span><br><span class="line">        <span class="comment">//call()方法就是指明哪个对象来调用方法</span></span><br><span class="line">        <span class="comment">//之前没使用call()方法时，默认是以window来调用方法，以致于window下会多个count=NaN的变量</span></span><br><span class="line">        foo.call(foo,i);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(foo.count);</span><br></pre></td></tr></table></figure></li>
<li>这下输出就正常了，由此可以看出其实<code>this</code>真正的含义是：__谁调用我，我指向谁__。</li>
<li>另外需要注意的是，不能通过<code>this</code>进行作用域穿越。<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">var</span> a = <span class="number">2</span>;</span><br><span class="line">    bar();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">bar</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">&#x27;a:&#x27;</span> + <span class="built_in">this</span>.a);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">foo();</span><br></pre></td></tr></table></figure></li>
<li>代码试图通过<code>this</code>关联<code>foo()</code>和<code>bar()</code>作用域，从而让<code>bar()</code>能访问<code>foo()</code>作用域中的变量，这是不可能实现的。<h3 id="1-3-this到底是什么"><a href="#1-3-this到底是什么" class="headerlink" title="1.3 this到底是什么"></a>1.3 this到底是什么</h3></li>
<li><code>this</code>在运行时绑定，而不是编写时，它的指向取决于函数的调用方式。</li>
<li>JavaScript当函数被调用时，会创建一个context（上下文/环境）记录，这个记录的内容包含函数在哪里被调用、函数的调用方式、 传入的参数等信息，而<code>this</code>就是函数context记录的其中一个属性。</li>
</ul>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://gitee.com/lianghuilin/murmur.git/reading_notes/%E3%80%8A%E4%BD%A0%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84JavaScript-%E4%B8%8A-%E3%80%8BS01E05/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/murmur/images/avatar.gif">
      <meta itemprop="name" content="William Leung">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="汇话板">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/murmur/reading_notes/%E3%80%8A%E4%BD%A0%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84JavaScript-%E4%B8%8A-%E3%80%8BS01E05/" class="post-title-link" itemprop="url">《你不知道的JavaScript(上)》S01E05</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>
      

      <time title="创建时间：2020-12-01 22:10:10 / 修改时间：23:07:26" itemprop="dateCreated datePublished" datetime="2020-12-01T22:10:10+08:00">2020-12-01</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/murmur/categories/%E4%B8%80%E5%91%A8%E4%B8%80%E7%AB%A0%E5%89%8D%E7%AB%AF%E4%B9%A6/" itemprop="url" rel="index"><span itemprop="name">一周一章前端书</span></a>
        </span>
    </span>

  
    <span class="post-meta-item" title="本文字数">
      <span class="post-meta-item-icon">
        <i class="far fa-file-word"></i>
      </span>
      <span class="post-meta-item-text">本文字数：</span>
      <span>5.2k</span>
    </span>
    <span class="post-meta-item" title="阅读时长">
      <span class="post-meta-item-icon">
        <i class="far fa-clock"></i>
      </span>
      <span class="post-meta-item-text">阅读时长 &asymp;</span>
      <span>5 分钟</span>
    </span>
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="第5章：作用域闭包"><a href="#第5章：作用域闭包" class="headerlink" title="第5章：作用域闭包"></a>第5章：作用域闭包</h1><h3 id="到底什么是闭包"><a href="#到底什么是闭包" class="headerlink" title="到底什么是闭包"></a>到底什么是闭包</h3><ul>
<li>本章讲解<code>闭包(Closures)</code>，它与作用域工作原理息息相关。</li>
<li>首先我用自己总结的三句话，简单说明什么是闭包：<ul>
<li>（1）首先我们要知道，<strong>变量的查找</strong> 规则 <strong>是由内到外的</strong> ；</li>
<li>（2）所以 <strong>子函数可以访问外部作用域</strong>  的变量；</li>
<li>（3）如果 <strong>把子函数赋值给外部变量</strong>  时，此时外部变量就 <strong>拥有</strong> 了可以 <strong>访问封闭数据包的能力</strong> ；</li>
</ul>
</li>
<li>一个简单的闭包示例<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">var</span> a = <span class="number">2</span>;</span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">bar</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(a); <span class="comment">//2</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> bar;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> baz = foo();</span><br></pre></td></tr></table></figure></li>
<li>分析上述代码：<ul>
<li><code>bar</code>函数能访问外部<code>foo</code>函数的作用域，将<code>bar</code>传递给外部变量<code>baz</code>来执行；</li>
<li>此时<code>bar</code>函数在原来定义的词法作用域之外执行，同时持有<code>foo</code>函数作用域的引用，这就叫作闭包。</li>
</ul>
</li>
<li>并且通过闭包的执行方式，<code>foo</code>函数在执行后，其作用域不会被立即销毁（毕竟<code>bar</code>函数还要用的啊）</li>
</ul>
<h3 id="闭包暴露的方式不止一种"><a href="#闭包暴露的方式不止一种" class="headerlink" title="闭包暴露的方式不止一种"></a>闭包暴露的方式不止一种</h3><ul>
<li>闭包函数除了可以直接赋值给外部变量，也可以通过执行外部函数，将闭包函数以参数传递的方式暴露出去。<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> <span class="function"><span class="title">foo</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">var</span> a = <span class="number">2</span>;</span><br><span class="line">    <span class="comment">//闭包函数</span></span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">baz</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(a);; <span class="comment">//2</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//执行外部函数，将闭包函数通过参数的方式传递进去</span></span><br><span class="line">    bar(baz)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> <span class="function"><span class="title">bar</span>(<span class="params">fn</span>)</span>&#123;</span><br><span class="line">    fn();</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

</li>
</ul>
<h3 id="闭包是最熟悉的陌生人"><a href="#闭包是最熟悉的陌生人" class="headerlink" title="闭包是最熟悉的陌生人"></a>闭包是最熟悉的陌生人</h3><ul>
<li>虽然闭包比较隐晦，但它绝不仅仅是一个好玩的玩具而已，在我们的代码中到处都有它的身影。</li>
<li>比如常见的定时函数<code>setTimeout()</code>：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">wait</span>(<span class="params">message</span>)</span>&#123;</span><br><span class="line">    <span class="built_in">setTimeout</span>(<span class="function"><span class="keyword">function</span> <span class="title">timer</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(message);</span><br><span class="line">    &#125;,<span class="number">1000</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
上述代码等价于：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//全局的setTimout函数准备就绪</span></span><br><span class="line"><span class="keyword">var</span> setTimout = <span class="function"><span class="keyword">function</span>(<span class="params">invokeFn</span>)</span>&#123;&#125;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">wait</span>(<span class="params">message</span>)</span>&#123;</span><br><span class="line">    <span class="comment">//timer内部函数拥有对mesage的访问权</span></span><br><span class="line">    <span class="keyword">var</span> timer = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(message);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//执行setTimeout()函数，并将timer以参数方式传递进去</span></span><br><span class="line">    setTimout(timer);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
<li>是不是有似曾相似的感觉？内部函数<code>timer()</code>具有外部函数<code>wait()</code>作用域中的<code>message</code>变量的引用，它就是闭包。</li>
<li>除了定时函数之外，jQuery代码也普遍的在使用闭包，不信你看下面的代码：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//参数传递一个name字符串，选择器字符串</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">setupBot</span>(<span class="params">name,selector</span>)</span>&#123;</span><br><span class="line">    <span class="comment">//通过选择器字符串初始化成jQuery对象</span></span><br><span class="line">    <span class="comment">//绑定点击事件</span></span><br><span class="line">    $(selector).click(<span class="function"><span class="keyword">function</span> <span class="title">activator</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="comment">//打印外部函数的name</span></span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">&#x27;Activating:&#x27;</span> + name);</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">setupBot(<span class="string">&#x27;Closure Bot 1&#x27;</span>,<span class="string">&#x27;#bot_1&#x27;</span>);</span><br><span class="line">setupBot(<span class="string">&#x27;Closure Bot 2&#x27;</span>,<span class="string">&#x27;#bot_2&#x27;</span>);</span><br></pre></td></tr></table></figure></li>
<li>其实无论何时何地，只要将函数当做参数进行传递，就有闭包的应用。</li>
<li>什么定时器、事件监听函数、Ajax请求回调函数、跨窗口通信、Web Workers等代码中，都普遍应用到了闭包。</li>
<li>它每天与我们擦肩而过，就好像那个最熟悉的陌生人。</li>
</ul>
<h3 id="闭包解决了什么问题"><a href="#闭包解决了什么问题" class="headerlink" title="闭包解决了什么问题"></a>闭包解决了什么问题</h3><h4 id="1-用闭包造块级作用域"><a href="#1-用闭包造块级作用域" class="headerlink" title="1. 用闭包造块级作用域"></a>1. 用闭包造块级作用域</h4><ul>
<li>我们先看问题： 我只是想依次输出循环的<code>i</code>（1 ~ 5）<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span>(<span class="keyword">var</span> i=<span class="number">1</span>;i&lt;=<span class="number">5</span>;i++)&#123;</span><br><span class="line">    setTimtou(<span class="function"><span class="keyword">function</span> <span class="title">timer</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(i);  </span><br><span class="line">    &#125;,<span class="number">0</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
<li>见鬼了，然而输出的结果却是五次6，这是为什么呢？</li>
<li>其根本原因是，定时器的回调函数永远在循环结束后才执行。</li>
<li>那你可能会想，哦，那我岂不是永远都不能在<code>for</code>循环中用定时器了？JavaScript真垃圾！诶诶，先别慌，我们来分析一下。</li>
<li>我们不是预期循环的每个迭代中，都有一个<code>i</code>的副本，然后输出它吗？通过闭包就能实现。<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span>(<span class="keyword">var</span> i=<span class="number">1</span>;i&lt;=<span class="number">5</span>;i++)&#123;</span><br><span class="line">    (<span class="function"><span class="keyword">function</span>(<span class="params">index</span>)</span>&#123;</span><br><span class="line">        setTimtou(<span class="function"><span class="keyword">function</span> <span class="title">timer</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">            <span class="built_in">console</span>.log(index);  </span><br><span class="line">        &#125;,<span class="number">0</span>)</span><br><span class="line">    &#125;)(i);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
<li>我们通过IIFE构造了一个块级作用域将<code>i</code>存了起来。</li>
<li>提到块级作用域，其实ES6的语法里还有一种更便捷的解决方式——<code>let</code>声明<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span>(<span class="keyword">let</span> i=<span class="number">1</span>;i&lt;=<span class="number">5</span>;i++)&#123;</span><br><span class="line">    setTimtou(<span class="function"><span class="keyword">function</span> <span class="title">timer</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(i);  </span><br><span class="line">    &#125;,<span class="number">0</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

</li>
</ul>
<h4 id="2-用闭包造模块"><a href="#2-用闭包造模块" class="headerlink" title="2. 用闭包造模块"></a>2. 用闭包造模块</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">CoolModule</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">var</span> something = <span class="string">&#x27;cool&#x27;</span>;</span><br><span class="line">    <span class="keyword">var</span> another = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>];</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">doSomething</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(something);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">doAnother</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(another.join(<span class="string">&quot;,&quot;</span>));</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//对外暴露内部函数</span></span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">        doSomething : doSomething,</span><br><span class="line">        doAnother : doAnother</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>上述代码演示了JavaScript模块暴露。通过调用<code>CoolModule</code>函数创建一个模块实例，<code>CoolModule</code>返回的对象中包含内部函数的引用，就相当于模块的公共API。</li>
<li>当然上述代码可以任意调用多次，重复返回新的模块实例，我们可以改成单例模式：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//通过一个IIFE函数来包装</span></span><br><span class="line"><span class="keyword">var</span> foo = (<span class="function"><span class="keyword">function</span> <span class="title">CoolModule</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">var</span> something = <span class="string">&#x27;cool&#x27;</span>;</span><br><span class="line">    <span class="keyword">var</span> another = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>];</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">doSomething</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(something);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">doAnother</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(another.join(<span class="string">&quot;,&quot;</span>));</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//对外暴露内部函数</span></span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">        doSomething : doSomething,</span><br><span class="line">        doAnother : doAnother</span><br><span class="line">    &#125;</span><br><span class="line">&#125;)();</span><br><span class="line"></span><br><span class="line">foo.doSomething();  <span class="comment">//cool</span></span><br><span class="line">foo.doAnother();    <span class="comment">//1,2,3</span></span><br></pre></td></tr></table></figure></li>
<li>模块的公共API不仅可以是内部私有变量的访问，也可以是修改私有变量的方法：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> foo = (<span class="function"><span class="keyword">function</span> <span class="title">CoolModule</span>(<span class="params">id</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">var</span> moduleId = id;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">showId</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(moduleId);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">uppcaseId</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        moduleId = moduleId.toUpperCase();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span>&#123;</span><br><span class="line">        showId : showId,</span><br><span class="line">        uppcaseId : uppcaseId</span><br><span class="line">    &#125;</span><br><span class="line">&#125;)(<span class="string">&#x27;fooModule&#x27;</span>);</span><br><span class="line"></span><br><span class="line">foo.showId();</span><br><span class="line">foo.uppcaseId();</span><br><span class="line">foo.showId();</span><br></pre></td></tr></table></figure></li>
<li>其实大多数模块管理机制本质是也是通过类似的方式来实现的，我们来尝试写一个简版的模块管理器：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 定义牛批哄哄的超级模块管理器</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">var</span> SuperModules = (<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">	<span class="comment">//所有的模块集合，以name作为key</span></span><br><span class="line">	<span class="keyword">var</span> moduleMap = &#123;&#125;;</span><br><span class="line">	</span><br><span class="line">	<span class="function"><span class="keyword">function</span> <span class="title">define</span>(<span class="params">name,deps,impl</span>)</span>&#123;</span><br><span class="line">		<span class="comment">//获取依赖</span></span><br><span class="line">		<span class="keyword">for</span>(<span class="keyword">var</span> i=<span class="number">0</span>;i&lt;deps.length;i++)&#123;</span><br><span class="line">			deps[i] = moduleMap[deps[i]]		</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="comment">//执行引入的模块，并以deps作为参数</span></span><br><span class="line">		moduleMap[name] = impl.apply(impl,deps);</span><br><span class="line">	&#125;</span><br><span class="line">	</span><br><span class="line">	<span class="function"><span class="keyword">function</span> <span class="title">get</span>(<span class="params">name</span>)</span>&#123;</span><br><span class="line">		reutrn moduleMap[name];</span><br><span class="line">	&#125;</span><br><span class="line">	</span><br><span class="line">	<span class="comment">//暴露公共API</span></span><br><span class="line">	<span class="keyword">return</span>&#123;</span><br><span class="line">		define : define,</span><br><span class="line">		get : get</span><br><span class="line">	&#125;</span><br><span class="line">&#125;)()</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 先定义一个bar模块</span></span><br><span class="line"><span class="comment"> * 没有依赖，impl是一个执行函数</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">SuperModules.define(<span class="string">&#x27;bar&#x27;</span>,[],<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">	<span class="function"><span class="keyword">function</span> <span class="title">hello</span>(<span class="params">name</span>)</span>&#123;</span><br><span class="line">		<span class="keyword">return</span> <span class="string">&#x27;let me introduce:&#x27;</span> + name;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">return</span> &#123;</span><br><span class="line">		hello : hello</span><br><span class="line">	&#125;</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 再定义一个foo模块</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">SuperModules.difine(<span class="string">&#x27;foo&#x27;</span>,[<span class="string">&#x27;bar&#x27;</span>],<span class="function"><span class="keyword">function</span>(<span class="params">bar</span>)</span>&#123;</span><br><span class="line">	<span class="keyword">var</span> hungry = <span class="string">&#x27;hippo&#x27;</span>;</span><br><span class="line">	</span><br><span class="line">	<span class="function"><span class="keyword">function</span> <span class="title">awesome</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">		<span class="built_in">console</span>.log(bar.hello(hungry).toUpperCase());</span><br><span class="line">	&#125;</span><br><span class="line">	</span><br><span class="line">	<span class="keyword">return</span> &#123;</span><br><span class="line">		awesome : awesome</span><br><span class="line">	&#125;</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 调用测试</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">var</span> bar = SuperModules.get(<span class="string">&#x27;bar&#x27;</span>);</span><br><span class="line"><span class="keyword">var</span> foo = SuperModules.get(<span class="string">&#x27;foo&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">//let me introduce: hippo</span></span><br><span class="line"><span class="built_in">console</span>.log(bar.hello(<span class="string">&#x27;hippo&#x27;</span>));</span><br><span class="line"><span class="comment">//let me introduce: HIPPO</span></span><br><span class="line">foo.awesome();</span><br></pre></td></tr></table></figure></li>
<li>是不是看起来比较复杂……不过不用担心，ES6以及添加了模块的语法支持！</li>
<li>ES6会将每个js文件当做独立的模块来处理，每个模块可以通过<code>import</code>关键字导入依赖的模块，或者通过<code>export</code>关键字导出API。你需要做的，只是拥抱ES6！</li>
<li><code>bar.js</code><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">hello</span>(<span class="params">name</span>)</span>&#123;</span><br><span class="line">	<span class="keyword">return</span> <span class="string">&#x27;let me introduce:&#x27;</span> + name;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">export</span> hello;</span><br></pre></td></tr></table></figure></li>
<li><code>foo.js</code><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> hello <span class="keyword">from</span> <span class="string">&#x27;bar&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> hungry = <span class="string">&#x27;hippo&#x27;</span>;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">awesome</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">	<span class="built_in">console</span>.log(bar.hello(hungry).toUpperCase());</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">export</span> awesome;</span><br></pre></td></tr></table></figure></li>
<li><code>baz.js</code><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span> foo <span class="keyword">from</span> <span class="string">&#x27;foo&#x27;</span>;</span><br><span class="line"><span class="built_in">module</span> bar <span class="keyword">from</span> <span class="string">&#x27;bar&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//let me introduce: hippo</span></span><br><span class="line"><span class="built_in">console</span>.log(bar.hello(<span class="string">&#x27;hippo&#x27;</span>));</span><br><span class="line"><span class="comment">//let me introduce: HIPPO</span></span><br><span class="line">foo.awesome();</span><br></pre></td></tr></table></figure>

</li>
</ul>
<h3 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h3><ul>
<li>内部函数可以访问外部函数的作用域，如果将内部函数暴露给外部变量时，或者说内部函数在定义的词法作用域之外执行时，就产生了闭包。</li>
<li>闭包是个非常强大的工具，可以用来实现模块模式。</li>
<li>模块的特征：<ul>
<li>为创建内部作用域而调用一个包装函数；</li>
<li>包装函数的返回值必须至少包含一个对内部函数的引用，这样就会创建涵盖整个包装函数内部作用域的闭包；</li>
</ul>
</li>
</ul>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://gitee.com/lianghuilin/murmur.git/reading_notes/%E3%80%8A%E4%BD%A0%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84JavaScript-%E4%B8%8A-%E3%80%8BS01E04/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/murmur/images/avatar.gif">
      <meta itemprop="name" content="William Leung">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="汇话板">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/murmur/reading_notes/%E3%80%8A%E4%BD%A0%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84JavaScript-%E4%B8%8A-%E3%80%8BS01E04/" class="post-title-link" itemprop="url">《你不知道的JavaScript(上)》S01E04</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>
      

      <time title="创建时间：2020-12-01 22:05:35 / 修改时间：23:07:27" itemprop="dateCreated datePublished" datetime="2020-12-01T22:05:35+08:00">2020-12-01</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/murmur/categories/%E4%B8%80%E5%91%A8%E4%B8%80%E7%AB%A0%E5%89%8D%E7%AB%AF%E4%B9%A6/" itemprop="url" rel="index"><span itemprop="name">一周一章前端书</span></a>
        </span>
    </span>

  
    <span class="post-meta-item" title="本文字数">
      <span class="post-meta-item-icon">
        <i class="far fa-file-word"></i>
      </span>
      <span class="post-meta-item-text">本文字数：</span>
      <span>1.3k</span>
    </span>
    <span class="post-meta-item" title="阅读时长">
      <span class="post-meta-item-icon">
        <i class="far fa-clock"></i>
      </span>
      <span class="post-meta-item-text">阅读时长 &asymp;</span>
      <span>1 分钟</span>
    </span>
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="第4章：提升"><a href="#第4章：提升" class="headerlink" title="第4章：提升"></a>第4章：提升</h1><h2 id="4-1-先有鸡还是先有蛋"><a href="#4-1-先有鸡还是先有蛋" class="headerlink" title="4.1 先有鸡还是先有蛋"></a>4.1 先有鸡还是先有蛋</h2><ul>
<li><p>分享两段代码，你认为会输出什么：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//先赋值后声明，再引用</span></span><br><span class="line">a = <span class="number">2</span>;</span><br><span class="line"><span class="keyword">var</span> a;</span><br><span class="line"><span class="built_in">console</span>.log(a);</span><br></pre></td></tr></table></figure>

<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//先引用，再声明变量</span></span><br><span class="line"><span class="built_in">console</span>.log(a);</span><br><span class="line"><span class="keyword">var</span> a = <span class="number">2</span>;</span><br></pre></td></tr></table></figure></li>
<li><p>答案揭晓：第一段代码会输出<code>2</code>，而第二段代码会输出<code>undefined</code></p>
</li>
<li><p>很多开发者会认为JavaScript代码是从上到下逐行执行的，但实际上并不完全是这样。</p>
</li>
<li><p>就好比先有鸡还是先有蛋，我们搞不清楚变量声明和赋值的先后问题。</p>
</li>
</ul>
<h2 id="4-2-编译器再度来袭"><a href="#4-2-编译器再度来袭" class="headerlink" title="4.2 编译器再度来袭"></a>4.2 编译器再度来袭</h2><ul>
<li><strong>实际上不管是变量还是函数，所有的声明都会在代码被执行前首先被处理。</strong></li>
<li>上面的第一段代码会这样进行处理：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//变量的声明语句被提升到最顶端</span></span><br><span class="line"><span class="keyword">var</span> a;</span><br><span class="line">a = <span class="number">2</span>;</span><br><span class="line"><span class="built_in">console</span>.log(a);</span><br></pre></td></tr></table></figure></li>
<li>上面的第二段代码会这样进行处理：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//只提升声明的语句，而赋值和引用的语句不会被提升</span></span><br><span class="line"><span class="keyword">var</span> a;</span><br><span class="line"><span class="built_in">console</span>.log(a);</span><br><span class="line">a = <span class="number">2</span>;</span><br></pre></td></tr></table></figure></li>
<li><strong>这个将声明语句从原本位置“移动”到顶端的过程，叫做“提升”。</strong></li>
<li>注意：如第二段代码所示，只有声明语句会被提升，而其他赋值/引用等运行逻辑的语句会留在原地。</li>
</ul>
<h2 id="4-3-函数优先"><a href="#4-3-函数优先" class="headerlink" title="4.3 函数优先"></a>4.3 函数优先</h2><ul>
<li><strong>所有声明都会被提升，但如果函数和变量同名时，函数优先被提升，然后才是变量：</strong><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//运行foo函数，最终输出1</span></span><br><span class="line">foo();</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> foo;    <span class="comment">//声明foo变量</span></span><br><span class="line"><span class="comment">//声明foo函数</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//为foo变量赋值一个函数</span></span><br><span class="line">foo = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="number">2</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
引擎会这样处理：<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//函数声明先被提升到顶部，重复的var声明被忽略</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//声明后紧接着就是调用语句</span></span><br><span class="line">foo();</span><br><span class="line"></span><br><span class="line"><span class="comment">//赋值语句在调用语句之后</span></span><br><span class="line">foo = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="number">2</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
<li><strong>变量提升的过程中，如果同名的函数声明，后者会覆盖前者。</strong><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 输出3</span></span><br><span class="line">foo();</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> foo = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="number">2</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="number">3</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
<li><strong>变量提升的过程，不受条件判断的限制。</strong><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">foo(); <span class="comment">//输出 b</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(<span class="literal">true</span>)&#123;</span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">&#x27;a&#x27;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;<span class="keyword">else</span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">&#x27;b&#x27;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

</li>
</ul>
<h2 id="4-4-小结"><a href="#4-4-小结" class="headerlink" title="4.4 小结"></a>4.4 小结</h2><ul>
<li>代码<code>var a = 2;</code>分为两个声明阶段，一个是编译阶段的变量声明，一个是执行阶段的赋值声明。</li>
<li>不管变量在作用域的什么位置，在代码被执行前都会先进行变量声明的处理。这个处理过程就像 <strong>把声明语句“移动”到作用域的顶端，我们称为“变量提升”。</strong></li>
<li><strong>只有声明语句会提示，而其他语句不会。</strong></li>
<li><strong>应当避免变量名的重复，否则会引发问题。</strong></li>
</ul>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>





  
  <nav class="pagination">
    <a class="extend prev" rel="prev" href="/murmur/page/3/"><i class="fa fa-angle-left" aria-label="上一页"></i></a><a class="page-number" href="/murmur/">1</a><span class="space">&hellip;</span><a class="page-number" href="/murmur/page/3/">3</a><span class="page-number current">4</span><a class="page-number" href="/murmur/page/5/">5</a><a class="extend next" rel="next" href="/murmur/page/5/"><i class="fa fa-angle-right" aria-label="下一页"></i></a>
  </nav>



      

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      const activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      const commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

    </div>
  </main>

  <footer class="footer">
    <div class="footer-inner">
      

      

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2023</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">William Leung</span>
</div>
<div class="wordcount">
  <span class="post-meta-item">
    <span class="post-meta-item-icon">
      <i class="fa fa-chart-line"></i>
    </span>
    <span title="站点总字数">142k</span>
  </span>
  <span class="post-meta-item">
    <span class="post-meta-item-icon">
      <i class="fa fa-coffee"></i>
    </span>
    <span title="站点阅读时长">2:09</span>
  </span>
</div>
<div class="busuanzi-count">
    <span id="busuanzi_container_site_uv">
      本站访客数<span id="busuanzi_value_site_uv"></span>人次
    </span>
    <span id="busuanzi_container_site_pv">
        本站总访问量<span id="busuanzi_value_site_pv"></span>次
    </span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.js.org/" class="theme-link" rel="noopener" target="_blank">NexT.Gemini</a> 强力驱动
  </div>

    </div>
  </footer>

  
  <script src="//cdn.jsdelivr.net/npm/animejs@3.2.1/lib/anime.min.js"></script>
<script src="/murmur/js/utils.js"></script><script src="/murmur/js/motion.js"></script><script src="/murmur/js/next-boot.js"></script>

  
















  <script>
    NProgress.configure({
      showSpinner: true
    });
    NProgress.start();
    document.addEventListener('readystatechange', () => {
      if (document.readyState === 'interactive') {
        NProgress.inc(0.8);
      }
      if (document.readyState === 'complete') {
        NProgress.done();
      }
    });
    document.addEventListener('pjax:send', () => {
      NProgress.start();
    });
    document.addEventListener('pjax:success', () => {
      NProgress.done();
    });
  </script>


  
  <script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>








  

  

</body>
</html>
